diff --git a/src/Avalonia.ReactiveUI/ViewModelViewHost.cs b/src/Avalonia.ReactiveUI/ViewModelViewHost.cs
index c88323d674..16dee00ebc 100644
--- a/src/Avalonia.ReactiveUI/ViewModelViewHost.cs
+++ b/src/Avalonia.ReactiveUI/ViewModelViewHost.cs
@@ -3,7 +3,7 @@ using System.Reactive.Disposables;
using ReactiveUI;
using Splat;
-namespace Avalonia.ReactiveUI
+namespace Avalonia.ReactiveUI
{
///
/// This content control will automatically load the View associated with
@@ -18,6 +18,12 @@ namespace Avalonia.ReactiveUI
public static readonly AvaloniaProperty
/// ViewModel to which the user navigates.
- private void NavigateToViewModel(object? viewModel)
+ /// The contract for view resolution.
+ private void NavigateToViewModel(object? viewModel, string? contract)
{
if (viewModel == null)
{
@@ -57,17 +73,33 @@ namespace Avalonia.ReactiveUI
Content = DefaultContent;
return;
}
-
+
var viewLocator = ViewLocator ?? global::ReactiveUI.ViewLocator.Current;
- var viewInstance = viewLocator.ResolveView(viewModel);
+ var viewInstance = viewLocator.ResolveView(viewModel, contract);
if (viewInstance == null)
{
- this.Log().Warn($"Couldn't find view for '{viewModel}'. Is it registered? Falling back to default content.");
+ if (contract == null)
+ {
+ this.Log().Warn($"Couldn't find view for '{viewModel}'. Is it registered? Falling back to default content.");
+ }
+ else
+ {
+ this.Log().Warn($"Couldn't find view with contract '{contract}' for '{viewModel}'. Is it registered? Falling back to default content.");
+ }
+
Content = DefaultContent;
return;
}
-
- this.Log().Info($"Ready to show {viewInstance} with autowired {viewModel}.");
+
+ if (contract == null)
+ {
+ this.Log().Info($"Ready to show {viewInstance} with autowired {viewModel}.");
+ }
+ else
+ {
+ this.Log().Info($"Ready to show {viewInstance} with autowired {viewModel} and contract '{contract}'.");
+ }
+
viewInstance.ViewModel = viewModel;
if (viewInstance is IStyledElement styled)
styled.DataContext = viewModel;
diff --git a/tests/Avalonia.ReactiveUI.UnitTests/ViewModelViewHostTest.cs b/tests/Avalonia.ReactiveUI.UnitTests/ViewModelViewHostTest.cs
index 35d1cbf62d..858c476227 100644
--- a/tests/Avalonia.ReactiveUI.UnitTests/ViewModelViewHostTest.cs
+++ b/tests/Avalonia.ReactiveUI.UnitTests/ViewModelViewHostTest.cs
@@ -12,15 +12,23 @@ namespace Avalonia.ReactiveUI.UnitTests
public class FirstView : ReactiveUserControl { }
+ public class AlternativeFirstView : ReactiveUserControl { }
+
public class SecondViewModel : ReactiveObject { }
public class SecondView : ReactiveUserControl { }
+ public class AlternativeSecondView : ReactiveUserControl { }
+
+ public static string AlternativeViewContract => "AlternativeView";
+
public ViewModelViewHostTest()
{
Locator.CurrentMutable.RegisterConstant(new AvaloniaActivationForViewFetcher(), typeof(IActivationForViewFetcher));
Locator.CurrentMutable.Register(() => new FirstView(), typeof(IViewFor));
Locator.CurrentMutable.Register(() => new SecondView(), typeof(IViewFor));
+ Locator.CurrentMutable.Register(() => new AlternativeFirstView(), typeof(IViewFor), AlternativeViewContract);
+ Locator.CurrentMutable.Register(() => new AlternativeSecondView(), typeof(IViewFor), AlternativeViewContract);
}
[Fact]
@@ -67,5 +75,67 @@ namespace Avalonia.ReactiveUI.UnitTests
Assert.Equal(first, ((FirstView)host.Content).DataContext);
Assert.Equal(first, ((FirstView)host.Content).ViewModel);
}
+
+ [Fact]
+ public void ViewModelViewHost_View_Should_Stay_In_Sync_With_ViewModel_And_Contract()
+ {
+ var defaultContent = new TextBlock();
+ var host = new ViewModelViewHost
+ {
+ DefaultContent = defaultContent,
+ PageTransition = null
+ };
+
+ var root = new TestRoot
+ {
+ Child = host
+ };
+
+ Assert.NotNull(host.Content);
+ Assert.Equal(typeof(TextBlock), host.Content.GetType());
+ Assert.Equal(defaultContent, host.Content);
+
+ var first = new FirstViewModel();
+ host.ViewModel = first;
+
+ host.ViewContract = null;
+ Assert.NotNull(host.Content);
+ Assert.Equal(typeof(FirstView), host.Content.GetType());
+ Assert.Equal(first, ((FirstView)host.Content).DataContext);
+ Assert.Equal(first, ((FirstView)host.Content).ViewModel);
+
+ host.ViewContract = AlternativeViewContract;
+ Assert.NotNull(host.Content);
+ Assert.Equal(typeof(AlternativeFirstView), host.Content.GetType());
+ Assert.Equal(first, ((AlternativeFirstView)host.Content).DataContext);
+ Assert.Equal(first, ((AlternativeFirstView)host.Content).ViewModel);
+
+ var second = new SecondViewModel();
+ host.ViewModel = second;
+
+ host.ViewContract = null;
+ Assert.NotNull(host.Content);
+ Assert.Equal(typeof(SecondView), host.Content.GetType());
+ Assert.Equal(second, ((SecondView)host.Content).DataContext);
+ Assert.Equal(second, ((SecondView)host.Content).ViewModel);
+
+ host.ViewContract = AlternativeViewContract;
+ Assert.NotNull(host.Content);
+ Assert.Equal(typeof(AlternativeSecondView), host.Content.GetType());
+ Assert.Equal(second, ((AlternativeSecondView)host.Content).DataContext);
+ Assert.Equal(second, ((AlternativeSecondView)host.Content).ViewModel);
+
+ host.ViewModel = null;
+
+ host.ViewContract = null;
+ Assert.NotNull(host.Content);
+ Assert.Equal(typeof(TextBlock), host.Content.GetType());
+ Assert.Equal(defaultContent, host.Content);
+
+ host.ViewContract = AlternativeViewContract;
+ Assert.NotNull(host.Content);
+ Assert.Equal(typeof(TextBlock), host.Content.GetType());
+ Assert.Equal(defaultContent, host.Content);
+ }
}
-}
\ No newline at end of file
+}