导航到 MvvmCross 中已有的视图模型

问题描述

在我的应用中,我遇到了几种可以导航到已显示的视图模型的情况:

  1. 在 macOS 上,应用程序首选项应显示在单独的 NSWindow 中,该 Navigate<Settingsviewmodel>() 不会像在 UWP 或 ipadOS 中那样阻塞或覆盖其他窗口。因此,用户可以打开首选项,然后将它们保持打开状态(最小化或在其他窗口后面),然后使用热键/菜单/按钮再次打开它们。如何将 Navigate<Docviewmodel,DocPathParam>(docPathParam) 定向到窗口中已打开的视图而不是创建新视图?

  2. 我的应用程序具有类似于 IDE 的主从布局:左侧边栏中有一个大纲,右侧选项卡内有文档。用户可能会打开某个文档,然后在大纲中再次单击其名称,而不是通过其打开的选项卡切换到它。我考虑通过 Navigate() 打开新的文档选项卡,但在这种情况下如何捕捉已打开的选项卡?

或者我应该避免在这两种情况下调用 useState 方法,而是从特定平台的视图层检测打开的窗口和选项卡?

解决方法

在检查了 MvvmCross 的内部调用后,我得出结论,试图“注入”到它的导航堆栈中太复杂了,我宁愿自己解决这两个问题。

对于第一种情况,我创建了一个小的“View Director”类,用作导航到单实例窗口(如应用程序的设置)的代理。虽然这种方法打破了 MvvmCross 的“从 VM 导航”原则,但我认为这很好,因为无论如何窗口行为都是特定于平台的。

当 View Director 收到导航请求时,它会检查自定义 UniqueWindowPresentation 属性,然后要求我的自定义 View Presenter 将焦点放在所请求视图模型的先前打开的窗口中。如果 Presenter 找不到这样的窗口,则会发生常规的 MvvmCross 导航(并最终创建该窗口)。

public class MvxMacViewDirector : IMvxMacViewDirector
{
    readonly IMvxViewsContainer _viewContainer;
    readonly IMvxAltMacViewPresenter _viewPresenter;
    readonly IMvxNavigationService _navigationService;

    public MvxMacViewDirector()
    {
        _viewContainer = Mvx.IoCProvider.Resolve<IMvxViewsContainer>();
        _viewPresenter = (IMvxAltMacViewPresenter)Mvx.IoCProvider.Resolve<IMvxViewPresenter>();
        _navigationService = Mvx.IoCProvider.Resolve<IMvxNavigationService>();
    }

    public void ShowView<TViewModel>() where TViewModel: MvxViewModel
    {
        Type viewType = _viewContainer.GetViewType(typeof(TViewModel));

        if (viewType.GetCustomAttribute<UniqueWindowPresentationAttribute>() != null)
        {
            if (_viewPresenter.ShowPreviouslyOpenedWindow<TViewModel>() == false)
                _navigationService.Navigate<TViewModel>();
        }
        else
            _navigationService.Navigate<TViewModel>();
    }
}

自定义 View Presenter 中的方法如下所示:

public bool ShowPreviouslyOpenedWindow<T>() where T : MvxViewModel
{
    foreach (var item in Windows)
    {
        if (item.ContentViewController is MvxViewController viewController &&
            viewController.ViewModel.GetType() == typeof(T))
        {
            item.MakeKeyAndOrderFront(null);
            return true;
        }
    }

    return false;
}

第二个案例真的让我想到了基于 MvvmCross 的导航的适当用例。最后,我决定它不应该负责显示嵌套视图/VM(例如拆分视图的“详细信息”部分内的选项卡),因为这种行为过于依赖内容,无法在视图演示器中概括。相反,我直接从其父窗格的视图模型管理选项卡的切换和创建:PaneViewModel 通过 IMvxViewModelLoader 创建选项卡 VM,同时 PaneView 观察其 VM 的选项卡集合并通过IMvxMacViewCreator,然后将 VM 分配给它们。这与 MvvmCross 在内部为实例化视图+VM 对所做的非常相似。

因此,我仅将 MvvmCross 导航用于需要替换窗口的整个内容或拆分视图的“详细信息”部分的“根视图”情况。或者当我需要在顶部显示对话框/工作表叠加层时。嵌套在这些视图中的所有内容都从它们的视图模型内部进行协调。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...