diff --git a/src/Avalonia.ReactiveUI/ReactiveUserControl.cs b/src/Avalonia.ReactiveUI/ReactiveUserControl.cs new file mode 100644 index 0000000000..5df6399c38 --- /dev/null +++ b/src/Avalonia.ReactiveUI/ReactiveUserControl.cs @@ -0,0 +1,45 @@ +// Copyright (c) The Avalonia Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using Avalonia; +using Avalonia.VisualTree; +using Avalonia.Controls; +using ReactiveUI; + +namespace Avalonia +{ + /// + /// A ReactiveUI UserControl that implements + /// and will activate your ViewModel automatically if it supports activation. + /// + /// ViewModel type. + public class ReactiveUserControl : UserControl, IViewFor where TViewModel : class + { + public static readonly AvaloniaProperty ViewModelProperty = AvaloniaProperty + .Register, TViewModel>(nameof(ViewModel)); + + /// + /// Initializes a new instance of the class. + /// + public ReactiveUserControl() + { + DataContextChanged += (sender, args) => ViewModel = DataContext as TViewModel; + this.WhenActivated(disposables => { /* activate ViewModel */ }); + } + + /// + /// The ViewModel. + /// + public TViewModel ViewModel + { + get => GetValue(ViewModelProperty); + set => SetValue(ViewModelProperty, value); + } + + object IViewFor.ViewModel + { + get => ViewModel; + set => ViewModel = (TViewModel)value; + } + } +} \ No newline at end of file diff --git a/src/Avalonia.ReactiveUI/ReactiveWindow.cs b/src/Avalonia.ReactiveUI/ReactiveWindow.cs new file mode 100644 index 0000000000..c8b0c59d67 --- /dev/null +++ b/src/Avalonia.ReactiveUI/ReactiveWindow.cs @@ -0,0 +1,45 @@ +// Copyright (c) The Avalonia Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using Avalonia; +using Avalonia.VisualTree; +using Avalonia.Controls; +using ReactiveUI; + +namespace Avalonia +{ + /// + /// A ReactiveUI Window that implements + /// and will activate your ViewModel automatically if it supports activation. + /// + /// ViewModel type. + public class ReactiveWindow : Window, IViewFor where TViewModel : class + { + public static readonly AvaloniaProperty ViewModelProperty = AvaloniaProperty + .Register, TViewModel>(nameof(ViewModel)); + + /// + /// Initializes a new instance of the class. + /// + public ReactiveWindow() + { + DataContextChanged += (sender, args) => ViewModel = DataContext as TViewModel; + this.WhenActivated(disposables => { /* activate ViewModel */ }); + } + + /// + /// The ViewModel. + /// + public TViewModel ViewModel + { + get => GetValue(ViewModelProperty); + set => SetValue(ViewModelProperty, value); + } + + object IViewFor.ViewModel + { + get => ViewModel; + set => ViewModel = (TViewModel)value; + } + } +} \ No newline at end of file diff --git a/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs b/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs index bda55276a8..cdf19c067d 100644 --- a/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs +++ b/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs @@ -23,7 +23,8 @@ namespace Avalonia public TestUserControlWithWhenActivated() { - this.WhenActivated(disposables => { + this.WhenActivated(disposables => + { Active = true; Disposable .Create(() => Active = false) @@ -38,7 +39,8 @@ namespace Avalonia public TestWindowWithWhenActivated() { - this.WhenActivated(disposables => { + this.WhenActivated(disposables => + { Active = true; Disposable .Create(() => Active = false) @@ -47,6 +49,42 @@ namespace Avalonia } } + public class ActivatableViewModel : ISupportsActivation + { + public ViewModelActivator Activator { get; } + + public bool IsActivated { get; private set; } + + public ActivatableViewModel() + { + Activator = new ViewModelActivator(); + this.WhenActivated(disposables => + { + IsActivated = true; + Disposable + .Create(() => IsActivated = false) + .DisposeWith(disposables); + }); + } + } + + public class ActivatableWindow : ReactiveWindow + { + public ActivatableWindow() { } + } + + public class ActivatableUserControl : ReactiveUserControl + { + public ActivatableUserControl() { } + } + + public AvaloniaActivationForViewFetcherTest() + { + Locator.CurrentMutable.RegisterConstant( + new AvaloniaActivationForViewFetcher(), + typeof(IActivationForViewFetcher)); + } + [Fact] public void Visual_Element_Is_Activated_And_Deactivated() { @@ -86,10 +124,6 @@ namespace Avalonia [Fact] public void Activation_For_View_Fetcher_Should_Support_When_Activated() { - Locator.CurrentMutable.RegisterConstant( - new AvaloniaActivationForViewFetcher(), - typeof(IActivationForViewFetcher)); - var userControl = new TestUserControlWithWhenActivated(); Assert.False(userControl.Active); @@ -104,10 +138,6 @@ namespace Avalonia [Fact] public void Activation_For_View_Fetcher_Should_Support_Windows() { - Locator.CurrentMutable.RegisterConstant( - new AvaloniaActivationForViewFetcher(), - typeof(IActivationForViewFetcher)); - using (var application = UnitTestApplication.Start(TestServices.MockWindowingPlatform)) { var window = new TestWindowWithWhenActivated(); @@ -120,5 +150,37 @@ namespace Avalonia Assert.False(window.Active); } } + + [Fact] + public void Activatable_Window_View_Model_Is_Activated_And_Deactivated() + { + using (var application = UnitTestApplication.Start(TestServices.MockWindowingPlatform)) + { + var viewModel = new ActivatableViewModel(); + var window = new ActivatableWindow { ViewModel = viewModel }; + Assert.False(viewModel.IsActivated); + + window.Show(); + Assert.True(viewModel.IsActivated); + + window.Close(); + Assert.False(viewModel.IsActivated); + } + } + + [Fact] + public void Activatable_User_Control_View_Model_Is_Activated_And_Deactivated() + { + var root = new TestRoot(); + var viewModel = new ActivatableViewModel(); + var control = new ActivatableUserControl { ViewModel = viewModel }; + Assert.False(viewModel.IsActivated); + + root.Child = control; + Assert.True(viewModel.IsActivated); + + root.Child = null; + Assert.False(viewModel.IsActivated); + } } } \ No newline at end of file