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