[Windows] Prism 8.0 入门(下):Prism.Wpf 和 Prism.Unity

以前做 WPF 和 Silverlight/Xamarin 项目的时候,我有时会把 ViewModel 和 View 放在不同的项目,ViewModel 使用 可移植类库项目,这样 ViewModel 就与 UI 平台无关,实现了代码复用。这样做还可以强制 View 和 ViewModel 解耦。

现在,即使在只写 WPF 项目的情况下,但为了强制 ViewModel 和 View 假装是陌生人,做到不留后路,我也倾向于把 View 和 ViewModel 放到不同项目,并且 ViewModel 使用 .Net Standard 作为目标框架。我还会假装下个月 UWP 就要崛起了,我手头的 WPF 项目中的 ViewModel 要做到平台无关,方便我下个月把项目移植到 UWP 项目中。

但如果要使用 Prism 构建 MVVM 程序的话,上面这些根本不现实。首先,Prism 做不到平台无关,它针对不同的平台提供了不同的包,分别是:

针对 WPF 的 Prism.Wpf

针对 Xamarin Forms 的 Prism.Forms

针对 Uno 平台的 Prism.Uno

其次,根本就没有针对 UWP 的 Prism.Windows(UWP 还有未来,忍住别哭)。

所以,除非只使用 Prism.Core,否则要将 ViewModel 项目共享给多个平台有点困难,毕竟用在 WPF 项目的 Prism.Wpf 本身就是个 Wpf 类库。

现在“编写平台无关的 ViewModel 项目”这个话题就与 Prism 无关了,再把 Prism.Unity 和 Prism.Wpf 选为代表(毕竟这个组合比其它组合下载量多些),这篇文章就只用它们作为 Prism 入门的学习对象。

[Windows] Prism 8.0 入门(下):Prism.Wpf 和 Prism.Unity

Prism.Core、Prism.Wpf 和 Prism.Unity 的依赖关系如上所示。其中 Prism.Core 实现了 MVVM 的核心功能,它是一个与平台无关的项目。Prism.Wpf 里包含了 Dialog Service、Region、Module 和导航等几个模块,都是些用在 WPF 的功能。Prism.Unity 本身没几行代码,它表示为 Prism.Wpf 选择了 UnityContainer 作为 IOC 容器。(另外还有 Prism.DryIoc 可以选择,但从下载量看 Prism.Unity 是主流。)

就算只学习 Prism.Wpf,可它的模块很多,一篇文章实在塞不下。我选择了 Dialog Service 作为代表,因为它的实现思想和其它的差不多,而且弹窗还是 WPF 最常见的操作。这篇文章将通过以下内容讲解如何使用 Prism.Wpf 构建一个 WPF 程序:

PrismApplication

RegisterTypes

XAML ContainerProvider

ViewModelLocator

Dialog Service

Prism 的最新版本是 8.0.0.1909。由于 Prism.Unity 依赖 Prism.Wpf,所以只需安装 Prism.Unity:

Install-Package Prism.Unity -Version 8.0.0.1909

2. PrismApplication

安装好 Prism.Wpf 和 Prism.Unity 后,下一步要做的是将 App.xaml 的类型替换为 PrismApplication。

<prism:PrismApplication x:Class="PrismTest.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:prism="http://prismlibrary.com/"> <Application.Resources> </Application.Resources> </prism:PrismApplication>

上面是修改过的 App.xaml,将 Application 改为 prism:PrismApplication,并且移除了 StartupUri="MainWindow.xaml"。

接下来不要忘记修改 App.xaml.cs:

public partial class App : PrismApplication { public App() { } protected override Window CreateShell() => Container.Resolve<ShellWindow>(); }

PrismApplication 不使用 StartupUri ,而是使用 CreateShell 方法创建主窗口。CreateShell 是必须实现的抽象函数。PrismApplication 提供了 Container 属性,CreateShell 函数里通常使用 Container 创建主窗口。

3. RegisterTypes

其实在使用 CreateShell 函数前,首先必须实现另一个抽象函数 RegisterTypes。由于 Prism.Wpf 相当依赖于 IOC,所以要现在 PrismApplication 里注册必须的类型或依赖。PrismApplication 里已经预先注册了 DialogService、EventAggregator、RegionManager 等必须的类型(在 RegisterRequiredTypes 函数里),其它类型可以在 RegisterTypes 里注册。它看起来像这样:

protected override void RegisterTypes(IContainerRegistry containerRegistry) { // Core Services // App Services // Views containerRegistry.RegisterForNavigation<BlankPage, BlankViewModel>(PageKeys.Blank); containerRegistry.RegisterForNavigation<MainPage, MainViewModel>(PageKeys.Main); containerRegistry.RegisterForNavigation<ShellWindow, ShellViewModel>(); // Configuration var configuration = BuildConfiguration(); // Register configurations to IoC containerRegistry.RegisterInstance<IConfiguration>(configuration); } 4. XAML ContainerProvider

在 XAML 中直接实例化 ViewModel 并设置 DataContext 是 View 和 ViewModel 之间建立关联的最基本的方法:

<UserControl.DataContext> <viewmodels:MainViewModel/> </UserControl.DataContext>

但现实中很难这样做,因为相当一部分 ViewModel 都会在构造函数中注入依赖,而 XAML 只能实例化具有无参数构造函数的类型。为了解决这个问题,Prism 提供了 ContainerProvider 这个工具,通过设置 Type 或 Name 从 Container 中解析请求的类型,它的用法如下:

<TextBlock Text="{Binding Path=Foo, Converter={prism:ContainerProvider {x:Type local:MyConverter}}}" /> <Window> <Window.DataContext> <prism:ContainerProvider Type="{x:Type local:MyViewModel}" /> </Window.DataContext> </Window> 5. ViewModelLocator

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zwxwyg.html