From 02bdbd5823ba5a8eeb176bcec081200f4c87fbba Mon Sep 17 00:00:00 2001 From: artyom Date: Thu, 22 Oct 2020 18:29:08 +0300 Subject: [PATCH] Properly handle context initialization --- .../ReactiveUserControl.cs | 9 ++- src/Avalonia.ReactiveUI/ReactiveWindow.cs | 9 ++- .../ReactiveUserControlTest.cs | 58 ++++++++++++++++- .../ReactiveWindowTest.cs | 62 ++++++++++++++++++- 4 files changed, 132 insertions(+), 6 deletions(-) diff --git a/src/Avalonia.ReactiveUI/ReactiveUserControl.cs b/src/Avalonia.ReactiveUI/ReactiveUserControl.cs index a401d62a06..5430d288bc 100644 --- a/src/Avalonia.ReactiveUI/ReactiveUserControl.cs +++ b/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 /// 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); } /// diff --git a/src/Avalonia.ReactiveUI/ReactiveWindow.cs b/src/Avalonia.ReactiveUI/ReactiveWindow.cs index 896795f42d..0a25d66af8 100644 --- a/src/Avalonia.ReactiveUI/ReactiveWindow.cs +++ b/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 /// 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); } /// diff --git a/tests/Avalonia.ReactiveUI.UnitTests/ReactiveUserControlTest.cs b/tests/Avalonia.ReactiveUI.UnitTests/ReactiveUserControlTest.cs index 026a08874f..5d257f75f2 100644 --- a/tests/Avalonia.ReactiveUI.UnitTests/ReactiveUserControlTest.cs +++ b/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 { } @@ -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); + } } } diff --git a/tests/Avalonia.ReactiveUI.UnitTests/ReactiveWindowTest.cs b/tests/Avalonia.ReactiveUI.UnitTests/ReactiveWindowTest.cs index 7612c07aae..18a8a33f09 100644 --- a/tests/Avalonia.ReactiveUI.UnitTests/ReactiveWindowTest.cs +++ b/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 { } @@ -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); + } + } } }