Browse Source

Add ReactiveUserControl and ReactiveWindow

artyom 7 years ago
parent
commit
d2be495bde

+ 45 - 0
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
+{
+    /// <summary>
+    /// A ReactiveUI UserControl that implements <see cref="IViewFor{TViewModel}"/> 
+    /// and will activate your ViewModel automatically if it supports activation.
+    /// </summary>
+    /// <typeparam name="TViewModel">ViewModel type.</typeparam>
+    public class ReactiveUserControl<TViewModel> : UserControl, IViewFor<TViewModel> where TViewModel : class
+    {
+        public static readonly AvaloniaProperty<TViewModel> ViewModelProperty = AvaloniaProperty
+            .Register<ReactiveWindow<TViewModel>, TViewModel>(nameof(ViewModel));
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="ReactiveUserControl{TViewModel}"/> class.
+        /// </summary>
+        public ReactiveUserControl()
+        {
+            DataContextChanged += (sender, args) => ViewModel = DataContext as TViewModel;
+            this.WhenActivated(disposables => {  /* activate ViewModel */ });
+        }
+
+        /// <summary>
+        /// The ViewModel.
+        /// </summary>
+        public TViewModel ViewModel
+        {
+            get => GetValue(ViewModelProperty);
+            set => SetValue(ViewModelProperty, value);
+        }
+
+        object IViewFor.ViewModel
+        {
+            get => ViewModel;
+            set => ViewModel = (TViewModel)value;
+        }
+    }
+}

+ 45 - 0
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 
+{
+    /// <summary>
+    /// A ReactiveUI Window that implements <see cref="IViewFor{TViewModel}"/>
+    /// and will activate your ViewModel automatically if it supports activation.
+    /// </summary>
+    /// <typeparam name="TViewModel">ViewModel type.</typeparam>
+    public class ReactiveWindow<TViewModel> : Window, IViewFor<TViewModel> where TViewModel : class
+    {
+        public static readonly AvaloniaProperty<TViewModel> ViewModelProperty = AvaloniaProperty
+            .Register<ReactiveWindow<TViewModel>, TViewModel>(nameof(ViewModel));
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="ReactiveWindow{TViewModel}"/> class.
+        /// </summary>
+        public ReactiveWindow()
+        {
+            DataContextChanged += (sender, args) => ViewModel = DataContext as TViewModel;
+            this.WhenActivated(disposables => { /* activate ViewModel */ });
+        }
+            
+        /// <summary>
+        /// The ViewModel.
+        /// </summary>
+        public TViewModel ViewModel
+        {
+            get => GetValue(ViewModelProperty);
+            set => SetValue(ViewModelProperty, value);
+        }
+
+        object IViewFor.ViewModel
+        {
+            get => ViewModel;
+            set => ViewModel = (TViewModel)value;
+        }
+    }
+}

+ 72 - 10
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<ActivatableViewModel>
+        {
+            public ActivatableWindow() { }
+        }
+
+        public class ActivatableUserControl : ReactiveUserControl<ActivatableViewModel>
+        {
+            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);
+        }
     }
 }