使用 Prism 的复合应用程序(WPF)- 第 3 部分





5.00/5 (15投票s)
在本篇文章中,我们将学习如何在 WPF 应用程序中使用 Prism 的 EventAggregator 和 IActiveAware 接口及其用法。这是文章系列的第二部分,共有三部分。
引言
在本篇文章中,我们将学习如何在 WPF 应用程序中使用 Prism 的 EventAggregator 和 IActiveAware 接口及其用法。这是文章系列的第二部分,共有三部分。
在第一部分中,我们创建了 Shell 并定义了在 Shell 的区域中加载模块的区域。在第二部分中,我们创建了通用的基础设施和五个模块。到目前为止,我们能够在新应用程序启动时以及按需加载模块,并能够将请求保存到数据库(XML 文件)。正如我们在第二部分最后一部分中所讨论的,“所有请求”模块存在一个问题。在这一部分,我们将修复该问题并满足另一个要求。
注意:如果您还没有阅读过第一部分和第二部分,我建议您先访问它们,因为它们将为您提供更好的上下文,并帮助您更好地理解本文。
目录
- 最终演示概述
- 最终演示的架构
- 什么是Prism
- 什么是Shell
- 创建Shell和定义区域
- 创建通用基础设施
- 创建模块
- 导航模块
- 员工模块
- 软件模块
- 硬件模块
- 请求模块
- 加载模块
- 演示应用程序中的问题
- 什么是事件聚合器
- 事件聚合器的使用
- 什么是IActiveAware接口
- IActiveAware的使用
问题回顾
我们在上一个演示应用程序中遗留了一个问题,即“所有请求”模块在第二部分最后一部分中讨论的。一旦“所有请求”模块首次加载,组合框就会显示与输入的员工 ID 相关联的正确请求 ID。但下次更改员工 ID 时,组合框不会显示与最新员工 ID 相关联的正确请求 ID。
根据我们的要求,每当员工 ID 更改时,“所有请求”模块的组合框需要显示与最近输入的员工 ID 相关联的所有请求 ID。为了实现这一点,我们需要通知“所有请求”模块。这个问题可以通过 EventAggregator 来解决。
什么是 EventAggregator
EventAggregator 提供了一个全局服务,用于在应用程序中发布或订阅事件。EventAggregator 是一个实现 IEventAggregator 接口的类。它属于 Microsoft.Practices.Prism.dll 中的“Microsoft.Practices.Prism.Events”命名空间。EventAggregator 在事件的发布者和订阅者对象之间充当调解者。无论哪个对象想要发布任何事件,都需要通过指定事件的参数(如果存在)与 EventAggregator 进行通信。无论哪个对象订阅该事件,EventAggregator 都会通过发布的参数(事件的负载)通知它。
下面的截图展示了 EventAggregator 与多个发布者和订阅者的一般图示描述。
在演示应用程序中,EmployeeViewModel 扮演发布者的角色,RequestViewModel 扮演订阅者的角色。EventAggregator 在 EmployeeViewModel 和 RequestViewModel 模块之间充当调解者。下面的截图展示了 EventAggregator 在演示应用程序中的实现。
如何使用 EventAggregator
要使用 EventAggregator,需要遵循一些标准步骤。我们将在本节中遵循这些步骤来使用 EventAggregator 以解决该问题。
- 首先,我们需要创建一个继承自“CompositePresentationEvent<TPayload>”的类,该类作为我们希望通知应用程序不同部分的事件。在文章系列的第一个部分,我们已经在 ITHelpDesk.Common.Infrastructure 项目的 Events 文件夹下添加了一个名为 EmployeeUpdatedEvent 的类。通常,此类事件在通用项目中声明(我们这里有 ITHelpDesk.Common.Infrastructure),因为这些事件旨在供应用程序中的多个项目使用。
当我们使用 Prism 应用程序的 EventAggregator 时,我们会得到一个名为“CompositePresentationEvent”的泛型类。由于它是一个泛型类,因此我们需要提供一个特定类型。此类型消息将传递给订阅者。因此,我们创建了 EmployeeUpdatedEvent 类并继承了 CompositePresentationEvent 类。通过 EmployeeUpdatedEvent,发布者(EmployeeViewModel)希望发布最新的员工 ID,它是一个整数。因此,EmployeeUpdatedEvent 的父类是 CompositePresentationEvent,它接受消息(事件负载)类型为 int。public class EmployeeUpdatedEvent : CompositePresentationEvent<int> { }
EmployeeViewModel 需要在员工 ID 更改时通知 RequestViewModel。为此,EmployeeViewModel 将发布一个名为 EmployeeUpdatedEvent 的事件,并附带最新的员工 ID。
- 发布者和订阅者对象必须使用 DI(依赖注入)容器提供的全局 EventAggregator。因此,这里我们将使用 Unity 容器提供的 EventAggregator,它在 UnityBootstrapper 类中初始化。这是开箱即用的,我们需要查看 UnityBootstrapper 类 ConfigureContainer 方法的代码。以下代码取自 Codeplex 的开源 Prism 库代码。
因此,全局 EventAggregator 的创建已经完成,因为我们的 ITHelpDeskBootstrapper 类继承自 UnityBootstrapper。
- EmployeeViewModel 将充当发布者,因此我们需要注入 EventAggregator 对象。现在,在 EmployeeViewModel 的构造函数中,我们注入了 IEventAggregator 并在构造函数本身中初始化了一个私有字段 eventAggregator,如下所示。
public EmployeeViewModel(IEmployeeService employeeService, IEventAggregator eventAggregator) { this.eventAggregator = eventAggregator; this.employeeService = employeeService; }
- 需要用更新后的值发布员工 ID。因此,在“EmployeeId”属性的 set 块中,“if”块之后,我们添加了一行代码用于发布 EmployeeUpdatedEvent。
public int EmployeeId { get { return employeeId; } set { if (value != 0) { employeeId = value; fetchEmployeeDetail(employeeId); RaisePropertyChanged("EmployeeId"); } eventAggregator.GetEvent<EmployeeUpdatedEvent>().Publish(value); } }
注意:决定发布和订阅事件的正确位置有点棘手。为了实现这一点,请在 set 块的第一个行(紧邻 if (value != 0) 行上方)替换 eventAggregator.GetEvent<EmployeeUpdatedEvent>().Publish(value); 代码。然后,我们可以看到 EventAggregator 将无法帮助我们解决我们遇到的问题。
- 任何数量的对象都可以订阅发布的事件(EmployeeUpdatedEvent)。让我们看看如何在 RequestViewModel 中订阅“EmployeeUpdatedEvent”事件。在 RequestViewModel 的构造函数中,我们订阅了 EmployeeUpdatedEvent。订阅时,我们传递了一个方法(employeeUpdateHandler),当事件发生时将调用该方法。在 employeeUpdateHandler 方法中,我们调用 fetchAllRequest 方法。正如我们可以从 fetchAllRequest 方法看到的,正在调用 EmployeeService 的 GetAllRequest 方法。这里我们在 employeeUpdateHandler 中获得了更新后的 empId,但我们没有使用它,因为我们知道在 EmployeeService 中,更新后的员工 ID 已经可用。以下是相关代码。
public RequestViewModel(IEmployeeService employeeService,IEventAggregator eventAggregator) { this.employeeService = employeeService; this.eventAggregator = eventAggregator; eventAggregator.GetEvent<EmployeeUpdatedEvent>().Subscribe(employeeUpdateHandler); fetchAllRequest(); } private void employeeUpdateHandler(int empId) { fetchAllRequest(); } private void fetchAllRequest() { List<Request> listOfRequest = employeeService.GetAllRequest(); AllRequests = new ObservableCollection<Request>(listOfRequest); }
Employee 服务的 GetAllRequest 方法通过传递 CurrentEmployeeId 来调用存储库的 GetAllRequest 方法。public List<Request> GetAllRequest() { return dataSource.GetAllRequest(CurrentEmployeeId); }
现在运行应用程序,组合框将始终显示与员工 ID 关联的请求 ID。
什么是 IActiveAware
IActiveAware 是一个接口,如果我们需要在视图激活或停用时执行某些操作,可以使用该接口。如果我们遵循 MVVM,应该在相应的 ViewModel 类中实现此接口。
此接口有两个成员:一个名为 IsActive 的属性和一个名为 IsActiveChanged 的事件。
// Summary: // Interface that defines if the object instance is active and notifies when // the activity changes. public interface IActiveAware { // Summary: // Gets or sets a value indicating whether the object is active. bool IsActive { get; set; } // Summary: // Notifies that the value for Microsoft.Practices.Prism.IActiveAware.IsActive // property has changed. event EventHandler IsActiveChanged; }
当视图或相应的 ViewModel 变为活动或非活动状态时,RegionActiveAwareBehavior 类将设置 IsActive 的新值。我们可以使用 IsActiveChanged 事件来通知相关方活动状态的更改。
新要求:我们将通过实现一个新场景来理解 IActiveAware。假设我们收到一个需求,即当用户重新访问任何模块时,其控件都必须刷新。这些控件不应保留旧值。重新访问时,它们必须是空的。
到目前为止,如果我们选择了一个软件模块,然后导航到其他模块,然后再次重新访问软件模块,我们将在控件中看到旧的选定值。下面的截图包含了突出显示我们在目前已开发演示中此行为的步骤。
在下一节中,我们将实现 IActiveAware 以满足此新需求。
如何使用 IActiveAware
首先,我们将在 SoftwareViewModel 类中实现 IActiveAware 接口。为了重置控件,我们需要将绑定的属性设置为默认值。让我们让 SoftwareViewModel 继承 IActiveAware 接口,并提供如下代码所示的实现。这里,refreshSoftwareModule 方法是从 IsActive 属性的 set 块调用的。在此方法中,我们将绑定的属性设置为默认值。
#region IActiveAware implementation bool active; public bool IsActive { get { return active; } set { if (active != value) { active = value; if (active == true) refreshSoftwareModule(); } } } private void refreshSoftwareModule() { SelectedSoftwareCategory = null; SelectedSoftwareName = null; Comment = null; } public event EventHandler IsActiveChanged; #endregion
现在运行应用程序,并按照上面截图中的 1 到 7 步骤进行操作。在第 7 步,我们将看到软件模块的控件已根据给定的新要求刷新,如下所示。
同样,我们也可以为硬件模块实现 IActiveAware 接口。
结论
在本文系列的这一部分,我们学习了如何在 Prism 应用程序中使用 EventAggregator 和 IActiveAware。这是文章系列的最后一部分,希望您觉得这个系列很有帮助。欢迎您的评论/建议和提问。谢谢。