65.9K
CodeProject 正在变化。 阅读更多。
Home

Prism for WPF:基础入门

starIconstarIconstarIconstarIconstarIcon

5.00/5 (5投票s)

2020年7月1日

CPOL

4分钟阅读

viewsIcon

33403

Prism 框架概述

引言

Prism 是一个框架,能够实现松耦合应用程序的开发,这些应用程序具有灵活性、可维护性和易于测试的特点。Prism 应用程序由模块组成——这些模块是功能单元,松耦合地封装了应用程序的整体功能。在团队协作的环境中,模块可以被单独开发、测试和部署,从而最大限度地减少跨团队依赖,并使团队或团队中的个人能够专注于应用程序的特定方面。Prism 可用于开发遵循 MVVM 设计模式的桌面或移动应用程序,因为它支持创建 WPF、Xamarin 和 UnoPlatform 项目。

背景

本文将通过一个展示虚构员工资料的示例应用程序,对 Prism for WPF 进行基础概述。该示例项目可以从 GitHub 克隆或下载。

示例应用程序。

示例应用程序包含四个项目:一个 WPF 应用程序项目;一个包含共享代码的类库;以及两个 Prism 模块。

提示:设置 Prism 项目的最简单方法是,首先安装 VS Prism Template Pack 扩展,并利用 Prism 项目模板。示例应用程序中的主项目是使用 Prism Blank App (WPF) 模板创建的,而模块则是使用 Prism Module (WPF) 模板添加的。

一些 Prism 项目模板。

Prism

Shell、Regions 和 Views

Prism 应用程序由一个shell组成,该 shell 托管应用程序的所有可视化组件。按照 Prism 的惯例,并且默认情况下,Prism 应用项目模板将 MainWindow 设置为应用程序的 shell。

public partial class App
{
    protected override Window CreateShell()
    {
        return Container.Resolve<MainWindow>();
    }

    ...
}		

Shell 包含一个或多个regions,模块可以在其中注入 views,而 view 又可以包含 regions,其他 views 可以在其中放置。下面的图表来自 Prism 文档,重点介绍了这种设置,

要将 view 放置到 region 中,模块会使用 RegionManager,它会跟踪应用程序中的所有 regions。示例应用程序只有一个名为 ContentRegion 的 region,

<mah:MetroWindow x:Class="StaffStuff.Views.MainWindow"
                 ...
                 xmlns:prism="http://prismlibrary.com/"
                 xmlns:common="clr-namespace:StaffStuff.Common;assembly=StaffStuff.Common"
                 xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls"
                 ...
                 prism:ViewModelLocator.AutoWireViewModel="True"
                 ...>
    ...

    <Grid>
        <ContentControl prism:RegionManager.RegionName=
                        "{x:Static common:RegionNames.ContentRegion}" />
    </Grid>
</mah:MetroWindow>	

示例应用程序有两个 views:一个显示包含一些员工详细信息的卡片的 user control;另一个 user control 显示特定员工的更多详细信息。后者 user control 还包含一个返回到第一个 view 的按钮。

这两个 views 位于一个名为 UIModule 的模块中,顾名思义,这个模块是 UI 相关的。(示例应用程序中的模块是按“水平层”组织的。如果应用程序是围绕垂直切片组织的,那么每个 view 都会在不同的模块中,并且这些模块将包含特定于 view 的功能。)

UIModule 实现 Prism 的 IModule 接口。该接口有两个方法:一个是在模块初始化时调用的方法,另一个用于类型注册(创建项目时,我将 Unity 设置为 IoC 容器。Prism 项目向导为您提供了使用 UnityDryIoc 的选项)。

using StaffStuff.UI.Views;
using Prism.Ioc;
using Prism.Modularity;
using Prism.Regions;
using StaffStuff.Common;

namespace StaffStuff.UI
{
    public class UIModule : IModule
    {
        public void OnInitialized(IContainerProvider containerProvider)
        {
            var regionManager = containerProvider.Resolve<IRegionManager>();
            regionManager.RequestNavigate(RegionNames.ContentRegion, nameof(StaffView));
        }

        public void RegisterTypes(IContainerRegistry containerRegistry)
        {
            containerRegistry.RegisterForNavigation<StaffView>();
            containerRegistry.RegisterForNavigation<StaffDetailsView>();
        }
    }
}		

OnInitialized() 中,我请求 RegionManagerStaffView 放置在 ContentRegion 中。因此,StaffView 将是应用程序启动时显示的默认 view。在 RegisterTypes() 中,我指定两个 views 都可用于导航。这使得可以使用 Prism 的导航功能在两个 views 之间来回移动。

View Models 和导航

每个 views 的 DataContext 都设置为一个 view model,该 view model 使用 Prism 的 ViewModelLocator 与 view 相关联。Prism 使用默认约定将 view model 与 view 相关联,该约定假定 view model 与 view 在同一个程序集中;位于 .ViewModels 子命名空间下;名称与 view 的名称对应;并且相应的 view 位于 .Views 子命名空间下。(如果您更喜欢使用不同的约定,可以指定一个 自定义 的。)UI 模块有两个 view models,每个都与一个特定的 view 相关联。

View models 继承一个基类,该基类继承 Prism 的 BindableBase 并实现其 INavigationAware 接口。

using Prism.Mvvm;
using Prism.Regions;

namespace StaffStuff.UI.ViewModels
{
    public class ViewModelBase : BindableBase, INavigationAware
    {
        protected IRegionManager RegionManager { get; }

        public ViewModelBase(IRegionManager regionManager)
        {
            RegionManager = regionManager;
        }

        public virtual bool IsNavigationTarget(NavigationContext navigationContext)
        {
            return true;
        }

        public virtual void OnNavigatedFrom(NavigationContext navigationContext)
        {
            
        }

        public virtual void OnNavigatedTo(NavigationContext navigationContext)
        {
            
        }
    }
}		

BindableBase 只是 Prism 对 INotifyPropertyChanged 的实现,而 INavigationAware 使 view model 能够参与导航过程。

StaffViewModel 包含一个 DelegateCommand,其执行方法指示 RegionManager 导航到员工详细信息 view——DelegateCommand 是 Prism 对 ICommand 接口的实现。

using System.Collections.Generic;
using Prism.Commands;
using Prism.Regions;
using StaffStuff.Common;
using StaffStuff.Common.Interfaces;
using StaffStuff.Common.Models;

namespace StaffStuff.UI.ViewModels
{
    public class StaffViewModel : ViewModelBase
    {
        private List<Employee> _employees;
        public List<Employee> Employees
        {
            get => _employees;
            set => SetProperty(ref _employees, value);
        }

        public DelegateCommand<Employee> EmployeeDetailsCommand { get; }

        public StaffViewModel(IStaffData staffData, IRegionManager regionManager) : 
                              base(regionManager)
        {
            Employees = staffData.GetEmployees();
            EmployeeDetailsCommand = new DelegateCommand<Employee>(StaffDetails);
        }

        private void StaffDetails(Employee employee)
        {
            var parameters = new NavigationParameters();
            parameters.Add(nameof(Employee), employee);

            RegionManager.RequestNavigate
                   (RegionNames.ContentRegion, "StaffDetailsView", parameters);
        }
    }
}		

在导航请求中会传递一个参数。此参数是一个 Employee 对象,它被添加到 NavigationParameters 集合中,并带有一个唯一标识它的键。导航到的 view 的 view model 可以使用该键来检索特定的对象。

using Prism.Commands;
using Prism.Regions;
using StaffStuff.Common.Models;

namespace StaffStuff.UI.ViewModels
{
    public class StaffDetailsViewModel : ViewModelBase
    {
        private Employee _employee;
        public Employee Employee
        {
            get => _employee; 
            set => SetProperty(ref _employee, value); 
        }

        private IRegionNavigationJournal _journal;

        public DelegateCommand GoBackCommand { get; }
        
        public StaffDetailsViewModel(IRegionManager regionManager) : base(regionManager)
        {
            GoBackCommand = new DelegateCommand(GoBack);
        }

        private void GoBack() => _journal.GoBack();

        public override void OnNavigatedTo(NavigationContext navigationContext)
        {
            Employee = navigationContext.Parameters[nameof(Employee)] as Employee;
            _journal = navigationContext.NavigationService.Journal;
        }
    }
}		

使用导航日志(navigation journal)进行返回到上一个 view 的导航。导航日志还有一个 GoForward() 方法,可用于向前导航。

模块注册

示例应用程序中的模块必须注册到 Prism 的 ModuleCatalog 中,以便应用程序加载它们。这在 App.xaml.cs 中完成。

public partial class App
{
    ...
    protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
    {
        moduleCatalog.AddModule<ServicesModule>();
        moduleCatalog.AddModule<UIModule>();
    }
}		

结论

我已经介绍了一些 Prism for WPF 的方面,但还有几个我没有重点介绍的领域;例如不同的导航策略、Prism 的事件聚合器(event aggregator)和复合命令(composite commands)。本文希望能为您提供一个基础平台,让您可以深入了解 Prism 的其他方面。我建议您查看 GitHub 上各种 Prism 示例 项目,并查阅 文档 以获得更广泛的视野。

历史

  • 2020 年 7 月 1 日:初次发布
© . All rights reserved.