Skip to content

简单导航2

简单

  • 不借用字符串去定位 ContentControl;
  • 注册视图时同时注册视图名-视图的关系,这样可以直接使用视图名进行导航;

导航

  • 使用的是 MSDI;
  • 在 viewModel 中绑定 ContentControl 的 Content 属性;
  • 依赖 CommunityToolkit.Mvvm 进行消息通知;
  • 通过视图名称查找视图实例;

注册

public static class DependencyInjectionExtensions
{
    public static IHostBuilder ConfigureViews(this IHostBuilder hostBuilder)
    {
        return hostBuilder.ConfigureServices(
            (sc) =>
            {
                sc.AddSingleton<MainWindow>();
                sc.AddSingleton<MainViewModel>();

                // navigation
                sc.AddForNavigation(typeof(VisibilityView), typeof(VisibilityViewModel));
                sc.AddForNavigation(typeof(GridSplitterView), typeof(GridSplitterViewModel));
                sc.AddForNavigation(typeof(CustomizedTabItemView), typeof(CustomizedTabItemViewModel));
                sc.AddForNavigation(typeof(MessageBoxView), typeof(MessageBoxViewModel));
                sc.AddForNavigation(typeof(DragDropView), typeof(DragDropViewModel));
                sc.AddForNavigation(typeof(ProgressMaskView), typeof(ProgressMaskViewModel));
                sc.AddForNavigation(typeof(DesignTimeDataView), typeof(DesignTimeDataViewModel));
                sc.AddForNavigation(typeof(ValidationView), typeof(ValidationViewModel));
                sc.AddForNavigation(typeof(PlayerView), typeof(PlayerViewModel));
            }
        );
    }

    public static void AddForNavigation(this IServiceCollection sc, Type view, Type viewModel)
    {
        var viewItem = new ViewRegistryItem()
        {
            Name = view.Name,
            ViewType = view,
            ViewModelType = viewModel
        };

        sc.AddSingleton(viewItem);

        sc.AddTransient(view);
        sc.AddTransient(viewModel);
    }
}

发出导航请求

partial void OnSelectedViewChanged(ViewItem? value)
{
    if (value is null)
    {
        return;
    }

    Messenger.Send(value.ViewName, Channels.NAVIGATION);
}

处理导航请求

private void Navigate(string viewName)
{
    var views = this.serviceProvider.GetService<IEnumerable<ViewRegistryItem>>();
    var viewItem = views?.LastOrDefault(v => v.Name == viewName);
    if (viewItem is null)
    {
        return;
    }

    var view = this.serviceProvider.GetService(viewItem.ViewType);
    if (view is null)
    {
        return;
    }

    ContentView = view;
}

总结

  • 可以使用字符串来避免导航时需要视图类型的耦合;
  • 在视图注册了的情况下,后续新增导航视图时只需,1. 添加导航目录;2. 发送请求导航消息;
  • 如何导航嵌套得很深的话,在多个 viewModel 中可能会重复实现相似的逻辑,考虑创建导航管理类,将导航的职责提取出来;

Ref

Github: orrest/Tests