using System; using System.Reactive.Disposables; using System.Reactive.Linq; using Avalonia.Animation; using Avalonia.Controls; using Avalonia.Styling; using Avalonia; using ReactiveUI; using Splat; namespace Avalonia.ReactiveUI { /// /// This control hosts the View associated with ReactiveUI RoutingState, /// and will display the View and wire up the ViewModel whenever a new /// ViewModel is navigated to. Nested routing is also supported. /// /// /// /// ReactiveUI routing consists of an IScreen that contains current /// RoutingState, several IRoutableViewModels, and a platform-specific /// XAML control called RoutedViewHost. /// /// /// RoutingState manages the ViewModel navigation stack and allows /// ViewModels to navigate to other ViewModels. IScreen is the root of /// a navigation stack; despite the name, its views don't have to occupy /// the whole screen. RoutedViewHost monitors an instance of RoutingState, /// responding to any changes in the navigation stack by creating and /// embedding the appropriate view. /// /// /// Place this control to a view containing your ViewModel that implements /// IScreen, and bind IScreen.Router property to RoutedViewHost.Router property. /// /// /// /// /// /// /// ]]> /// /// /// /// See /// ReactiveUI routing documentation website for more info. /// /// public class RoutedViewHost : TransitioningContentControl, IActivatableView, IEnableLogger { /// /// for the property. /// public static readonly StyledProperty RouterProperty = AvaloniaProperty.Register(nameof(Router)); /// /// Initializes a new instance of the class. /// public RoutedViewHost() { this.WhenActivated(disposables => { var routerRemoved = this .WhenAnyValue(x => x.Router) .Where(router => router == null)! .Cast(); this.WhenAnyValue(x => x.Router) .Where(router => router != null) .SelectMany(router => router!.CurrentViewModel) .Merge(routerRemoved) .Subscribe(NavigateToViewModel) .DisposeWith(disposables); }); } /// /// Gets or sets the of the view model stack. /// public RoutingState? Router { get => GetValue(RouterProperty); set => SetValue(RouterProperty, value); } /// /// Gets or sets the ReactiveUI view locator used by this router. /// public IViewLocator? ViewLocator { get; set; } /// /// Invoked when ReactiveUI router navigates to a view model. /// /// ViewModel to which the user navigates. private void NavigateToViewModel(object? viewModel) { if (Router == null) { this.Log().Warn("Router property is null. Falling back to default content."); Content = DefaultContent; return; } 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); if (viewInstance == null) { this.Log().Warn($"Couldn't find view for '{viewModel}'. Is it registered? Falling back to default content."); Content = DefaultContent; return; } this.Log().Info($"Ready to show {viewInstance} with autowired {viewModel}."); viewInstance.ViewModel = viewModel; if (viewInstance is IDataContextProvider provider) provider.DataContext = viewModel; Content = viewInstance; } } }