Prism 还提供了 ViewModelLocator,用于将 View 的 DataContext 设置为对应的 ViewModel:
<Window x:Class="Demo.Views.MainWindow" ... xmlns:prism="http://prismlibrary.com/" prism:ViewModelLocator.AutoWireViewModel="True">在将 View 的 ViewModelLocator.AutoWireViewModel 附加属性设置为 True 的同时,Prism 会为查找这个 View 对应的 ViewModel 类型,然后从 Container 中解析这个类型并设置为 View 的 DataContext。它首先查找 ViewModelLocationProvider 中已经使用 Register 注册的类型,Register 函数的使用方式如下:
ViewModelLocationProvider.Register<MainWindow, CustomViewModel>();如果类型未在 ViewModelLocationProvider 中注册,则根据约定好的命名方式找到 ViewModel 的类型,这是默认的查找逻辑的源码:
var viewName = viewType.FullName; viewName = viewName.Replace(".Views.", ".ViewModels."); var viewAssemblyName = viewType.GetTypeInfo().Assembly.FullName; var suffix = viewName.EndsWith("View") ? "Model" : "ViewModel"; var viewModelName = String.Format(CultureInfo.InvariantCulture, "{0}{1}, {2}", viewName, suffix, viewAssemblyName); return Type.GetType(viewModelName);例如 PrismTest.Views.MainView 这个类,对应的 ViewModel 类型就是 PrismTest.ViewModels.MainViewModel。
当然很多项目都不符合这个命名规则,那么可以在 App.xaml.cs 中重写 ConfigureViewModelLocator 并调用 ViewModelLocationProvider.SetDefaultViewTypeToViewModelTypeResolver 改变这个查找规则:
protected override void ConfigureViewModelLocator() { base.ConfigureViewModelLocator(); ViewModelLocationProvider.SetDefaultViewTypeToViewModelTypeResolver((viewType) => { var viewName = viewType.FullName.Replace(".ViewModels.", ".CustomNamespace."); var viewAssemblyName = viewType.GetTypeInfo().Assembly.FullName; var viewModelName = $"{viewName}ViewModel, {viewAssemblyName}"; return Type.GetType(viewModelName); }); } 6. Dialog ServicePrism 7 和 8 相对于以往的版本最大的改变在于 View 和 ViewModel 的交互,现在的处理方式变得更加易于使用,这篇文章以其中的 DialogService 作为代表讲解 Prism 如何实现 View 和 ViewModel 之间的交互。
DialogService 内部会调用 ViewModelLocator.AutoWireViewModel,所以使用 DialogService 调用的 View 无需添加这个附加属性。
以往在 WPF 中需要弹出一个窗口,首先新建一个 Window,然后调用 ShowDialog,ShowDialog 阻塞当前线程,直到弹出的 Window 关闭,这时候还可以拿到一个返回值,具体代码差不多是这样:
var window = new CreateUserWindow { Owner = this }; var dialogResult = window.ShowDialog(); if (dialogResult == true) { var user = window.User; //other code; }简单直接有用。但在 MVVM 模式中,开发者要假装自己不知道要调用的 View,甚至不知道要调用的 ViewModel。开发者只知道要执行的这个操作的名字,要传什么参数,拿到什么结果,至于具体由谁去执行,开发者要假装不知道(虽然很可能都是自己写的)。为了做到这种效果,Prism 提供了 IDialogService 接口。这个接口的具体实现已经在 PrismApplication 里注册了,用户通常只需要从构造函数里注入这个服务:
public MainWindowViewModel(IDialogService dialogService) { _dialogService = dialogService; }IDialogService 提供两组函数,分别是 Show 和 ShowDialog,对应非模态和模态窗口。它们的参数都一样:弹出的对话框的名称、传入的参数、对话框关闭时调用的回调函数:
void ShowDialog(string name, IDialogParameters parameters, Action<IDialogResult> callback);其中 IDialogResult 类型包含 ButtonResult 类型的 Result 属性和 IDialogParameters 类型的 Parameters 属性,前者用于标识关闭对话框的动作(Yes、No、Cancel等),后者可以传入任何类型的参数作为具体的返回结果。下面代码展示了一个基本的 ShowDialog 函数调用方式:
var parameters = new DialogParameters { { "UserName", "Admin" } }; _dialogService.ShowDialog("CreateUser", parameters, dialogResult => { if (dialogResult.Result == ButtonResult.OK) { var user = dialogResult.Parameters.GetValue<User>("User"); //other code } });为了让 IDialogService 知道上面代码中 “CreateUser” 对应的 View,需要在 \'App,xaml.cs\' 中的 RegisterTypes 函数中注册它对应的 Dialog:
containerRegistry.RegisterDialog<CreateUserView>("CreateUser");