问题描述
在我的应用中,我遇到了几种可以导航到已显示的视图模型的情况:
-
在 macOS 上,应用程序首选项应显示在单独的
NSWindow
中,该Navigate<Settingsviewmodel>()
不会像在 UWP 或 ipadOS 中那样阻塞或覆盖其他窗口。因此,用户可以打开首选项,然后将它们保持打开状态(最小化或在其他窗口后面),然后使用热键/菜单/按钮再次打开它们。如何将Navigate<Docviewmodel,DocPathParam>(docPathParam)
定向到窗口中已打开的视图而不是创建新视图? -
我的应用程序具有类似于 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 导航用于需要替换窗口的整个内容或拆分视图的“详细信息”部分的“根视图”情况。或者当我需要在顶部显示对话框/工作表叠加层时。嵌套在这些视图中的所有内容都从它们的视图模型内部进行协调。