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; } } }