csharpc-sharpdotnetxamlavaloniauicross-platformcross-platform-xamlavaloniaguimulti-platformuser-interfacedotnetcore
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
132 lines
4.9 KiB
132 lines
4.9 KiB
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
|
|
{
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// ReactiveUI routing consists of an IScreen that contains current
|
|
/// RoutingState, several IRoutableViewModels, and a platform-specific
|
|
/// XAML control called RoutedViewHost.
|
|
/// </para>
|
|
/// <para>
|
|
/// 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.
|
|
/// </para>
|
|
/// <para>
|
|
/// Place this control to a view containing your ViewModel that implements
|
|
/// IScreen, and bind IScreen.Router property to RoutedViewHost.Router property.
|
|
/// <code>
|
|
/// <![CDATA[
|
|
/// <rxui:RoutedViewHost
|
|
/// HorizontalAlignment="Stretch"
|
|
/// VerticalAlignment="Stretch"
|
|
/// Router="{Binding Router}">
|
|
/// <rxui:RoutedViewHost.DefaultContent>
|
|
/// <TextBlock Text="Default Content"/>
|
|
/// </rxui:RoutedViewHost.DefaultContent>
|
|
/// </rxui:RoutedViewHost>
|
|
/// ]]>
|
|
/// </code>
|
|
/// </para>
|
|
/// <para>
|
|
/// See <see href="https://reactiveui.net/docs/handbook/routing/">
|
|
/// ReactiveUI routing documentation website</see> for more info.
|
|
/// </para>
|
|
/// </remarks>
|
|
public class RoutedViewHost : TransitioningContentControl, IActivatableView, IEnableLogger
|
|
{
|
|
/// <summary>
|
|
/// <see cref="AvaloniaProperty"/> for the <see cref="Router"/> property.
|
|
/// </summary>
|
|
public static readonly StyledProperty<RoutingState?> RouterProperty =
|
|
AvaloniaProperty.Register<RoutedViewHost, RoutingState?>(nameof(Router));
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="RoutedViewHost"/> class.
|
|
/// </summary>
|
|
public RoutedViewHost()
|
|
{
|
|
this.WhenActivated(disposables =>
|
|
{
|
|
var routerRemoved = this
|
|
.WhenAnyValue(x => x.Router)
|
|
.Where(router => router == null)!
|
|
.Cast<object?>();
|
|
|
|
this.WhenAnyValue(x => x.Router)
|
|
.Where(router => router != null)
|
|
.SelectMany(router => router!.CurrentViewModel)
|
|
.Merge(routerRemoved)
|
|
.Subscribe(NavigateToViewModel)
|
|
.DisposeWith(disposables);
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the <see cref="RoutingState"/> of the view model stack.
|
|
/// </summary>
|
|
public RoutingState? Router
|
|
{
|
|
get => GetValue(RouterProperty);
|
|
set => SetValue(RouterProperty, value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the ReactiveUI view locator used by this router.
|
|
/// </summary>
|
|
public IViewLocator? ViewLocator { get; set; }
|
|
|
|
/// <summary>
|
|
/// Invoked when ReactiveUI router navigates to a view model.
|
|
/// </summary>
|
|
/// <param name="viewModel">ViewModel to which the user navigates.</param>
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|