using System;
using System.Reactive.Disposables;
using ReactiveUI;
using Splat;
namespace Avalonia.ReactiveUI
{
///
/// This content control will automatically load the View associated with
/// the ViewModel property and display it. This control is very useful
/// inside a DataTemplate to display the View associated with a ViewModel.
///
public class ViewModelViewHost : TransitioningContentControl, IViewFor, IEnableLogger
{
///
/// for the property.
///
public static readonly AvaloniaProperty ViewModelProperty =
AvaloniaProperty.Register(nameof(ViewModel));
///
/// for the property.
///
public static readonly StyledProperty ViewContractProperty =
AvaloniaProperty.Register(nameof(ViewContract));
///
/// Initializes a new instance of the class.
///
public ViewModelViewHost()
{
this.WhenActivated(disposables =>
{
this.WhenAnyValue(x => x.ViewModel, x => x.ViewContract)
.Subscribe(tuple => NavigateToViewModel(tuple.Item1, tuple.Item2))
.DisposeWith(disposables);
});
}
///
/// Gets or sets the ViewModel to display.
///
public object? ViewModel
{
get => GetValue(ViewModelProperty);
set => SetValue(ViewModelProperty, value);
}
///
/// Gets or sets the view contract.
///
public string? ViewContract
{
get => GetValue(ViewContractProperty);
set => SetValue(ViewContractProperty, value);
}
///
/// Gets or sets the view locator.
///
public IViewLocator? ViewLocator { get; set; }
///
/// Invoked when ReactiveUI router navigates to a view model.
///
/// ViewModel to which the user navigates.
/// The contract for view resolution.
private void NavigateToViewModel(object? viewModel, string? contract)
{
if (viewModel == null)
{
this.Log().Info("ViewModel is null. Falling back to default content.");
Content = DefaultContent;
return;
}
var viewLocator = ViewLocator ?? global::ReactiveUI.ViewLocator.Current;
var viewInstance = viewLocator.ResolveView(viewModel, contract);
if (viewInstance == null)
{
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;
}
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;
Content = viewInstance;
}
}
}