Browse Source

Properly handle context initialization

artyom 5 years ago
parent
commit
02bdbd5823

+ 7 - 2
src/Avalonia.ReactiveUI/ReactiveUserControl.cs

@@ -1,4 +1,6 @@
 using System;
+using System.Reactive.Disposables;
+using System.Reactive.Linq;
 using Avalonia;
 using Avalonia.VisualTree;
 using Avalonia.Controls;
@@ -22,10 +24,13 @@ namespace Avalonia.ReactiveUI
         /// </summary>
         public ReactiveUserControl()
         {
-            this.WhenAnyValue(x => x.DataContext)
-                .Subscribe(context => ViewModel = context as TViewModel);
+            this.WhenActivated(disposables => { });
             this.WhenAnyValue(x => x.ViewModel)
+                .Skip(1)
                 .Subscribe(model => DataContext = model);
+            this.WhenAnyValue(x => x.DataContext)
+                .Skip(1)
+                .Subscribe(context => ViewModel = context as TViewModel);
         }
 
         /// <summary>

+ 7 - 2
src/Avalonia.ReactiveUI/ReactiveWindow.cs

@@ -1,4 +1,6 @@
 using System;
+using System.Reactive.Disposables;
+using System.Reactive.Linq;
 using Avalonia;
 using Avalonia.VisualTree;
 using Avalonia.Controls;
@@ -22,10 +24,13 @@ namespace Avalonia.ReactiveUI
         /// </summary>
         public ReactiveWindow()
         {
-            this.WhenAnyValue(x => x.DataContext)
-                .Subscribe(context => ViewModel = context as TViewModel);
+            this.WhenActivated(disposables => { });
             this.WhenAnyValue(x => x.ViewModel)
+                .Skip(1)
                 .Subscribe(model => DataContext = model);
+            this.WhenAnyValue(x => x.DataContext)
+                .Skip(1)
+                .Subscribe(context => ViewModel = context as TViewModel);
         }
             
         /// <summary>

+ 57 - 1
tests/Avalonia.ReactiveUI.UnitTests/ReactiveUserControlTest.cs

@@ -1,3 +1,4 @@
+using System.Reactive.Disposables;
 using Avalonia.UnitTests;
 using ReactiveUI;
 using Splat;
@@ -7,7 +8,20 @@ namespace Avalonia.ReactiveUI.UnitTests
 {
     public class ReactiveUserControlTest
     {
-        public class ExampleViewModel : ReactiveObject { }
+        public class ExampleViewModel : ReactiveObject, IActivatableViewModel
+        {
+            public bool IsActive { get; private set; }
+
+            public ViewModelActivator Activator { get; } = new ViewModelActivator();
+
+            public ExampleViewModel() => this.WhenActivated(disposables =>
+            {
+                IsActive = true;
+                Disposable
+                    .Create(() => IsActive = false)
+                    .DisposeWith(disposables);
+            });
+        }
 
         public class ExampleView : ReactiveUserControl<ExampleViewModel> { }
 
@@ -44,5 +58,47 @@ namespace Avalonia.ReactiveUI.UnitTests
             Assert.Null(view.ViewModel);
             Assert.Null(view.DataContext);
         }
+
+        [Fact]
+        public void Should_Start_With_NotNull_Activated_ViewModel()
+        {
+            var root = new TestRoot();
+            var view = new ExampleView {ViewModel = new ExampleViewModel()};
+
+            Assert.False(view.ViewModel.IsActive);
+
+            root.Child = view;
+
+            Assert.NotNull(view.ViewModel);
+            Assert.NotNull(view.DataContext);
+            Assert.True(view.ViewModel.IsActive);
+
+            root.Child = null;
+
+            Assert.NotNull(view.ViewModel);
+            Assert.NotNull(view.DataContext);
+            Assert.False(view.ViewModel.IsActive);
+        }
+
+        [Fact]
+        public void Should_Start_With_NotNull_Activated_DataContext()
+        {
+            var root = new TestRoot();
+            var view = new ExampleView {DataContext = new ExampleViewModel()};
+
+            Assert.False(view.ViewModel.IsActive);
+
+            root.Child = view;
+
+            Assert.NotNull(view.ViewModel);
+            Assert.NotNull(view.DataContext);
+            Assert.True(view.ViewModel.IsActive);
+
+            root.Child = null;
+
+            Assert.NotNull(view.ViewModel);
+            Assert.NotNull(view.DataContext);
+            Assert.False(view.ViewModel.IsActive);
+        }
     }
 }

+ 61 - 1
tests/Avalonia.ReactiveUI.UnitTests/ReactiveWindowTest.cs

@@ -1,3 +1,4 @@
+using System.Reactive.Disposables;
 using Avalonia.UnitTests;
 using ReactiveUI;
 using Splat;
@@ -7,7 +8,20 @@ namespace Avalonia.ReactiveUI.UnitTests
 {
     public class ReactiveWindowTest
     {
-        public class ExampleViewModel : ReactiveObject { }
+        public class ExampleViewModel : ReactiveObject, IActivatableViewModel
+        {
+            public bool IsActive { get; private set; }
+
+            public ViewModelActivator Activator { get; } = new ViewModelActivator();
+
+            public ExampleViewModel() => this.WhenActivated(disposables =>
+            {
+                IsActive = true;
+                Disposable
+                    .Create(() => IsActive = false)
+                    .DisposeWith(disposables);
+            });
+        }
 
         public class ExampleWindow : ReactiveWindow<ExampleViewModel> { }
 
@@ -47,5 +61,51 @@ namespace Avalonia.ReactiveUI.UnitTests
                 Assert.Null(view.DataContext);
             }
         }
+
+        [Fact]
+        public void Should_Start_With_NotNull_Activated_ViewModel()
+        {
+            using (UnitTestApplication.Start(TestServices.StyledWindow))
+            {
+                var view = new ExampleWindow { ViewModel = new ExampleViewModel() };
+
+                Assert.False(view.ViewModel.IsActive);
+
+                view.Show();
+
+                Assert.NotNull(view.ViewModel);
+                Assert.NotNull(view.DataContext);
+                Assert.True(view.ViewModel.IsActive);
+
+                view.Close();
+
+                Assert.NotNull(view.ViewModel);
+                Assert.NotNull(view.DataContext);
+                Assert.False(view.ViewModel.IsActive);
+            }
+        }
+
+        [Fact]
+        public void Should_Start_With_NotNull_Activated_DataContext()
+        {
+            using (UnitTestApplication.Start(TestServices.StyledWindow))
+            {
+                var view = new ExampleWindow { DataContext = new ExampleViewModel() };
+
+                Assert.False(view.ViewModel.IsActive);
+
+                view.Show();
+
+                Assert.NotNull(view.ViewModel);
+                Assert.NotNull(view.DataContext);
+                Assert.True(view.ViewModel.IsActive);
+
+                view.Close();
+
+                Assert.NotNull(view.ViewModel);
+                Assert.NotNull(view.DataContext);
+                Assert.False(view.ViewModel.IsActive);
+            }
+        }
     }
 }