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

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

starIconstarIconstarIconstarIconstarIcon

5.00/5 (15投票s)

2015 年 12 月 5 日

CPOL

7分钟阅读

viewsIcon

39043

downloadIcon

1277

在本篇文章中,我们将学习如何在 WPF 应用程序中使用 Prism 的 EventAggregator 和 IActiveAware 接口及其用法。这是文章系列的第二部分,共有三部分。

引言

在本篇文章中,我们将学习如何在 WPF 应用程序中使用 Prism 的 EventAggregator 和 IActiveAware 接口及其用法。这是文章系列的第二部分,共有三部分。

在第一部分中,我们创建了 Shell 并定义了在 Shell 的区域中加载模块的区域。在第二部分中,我们创建了通用的基础设施和五个模块。到目前为止,我们能够在新应用程序启动时以及按需加载模块,并能够将请求保存到数据库(XML 文件)。正如我们在第二部分最后一部分中所讨论的,“所有请求”模块存在一个问题。在这一部分,我们将修复该问题并满足另一个要求。

注意:如果您还没有阅读过第一部分第二部分,我建议您先访问它们,因为它们将为您提供更好的上下文,并帮助您更好地理解本文。

目录

第一部分

  • 最终演示概述
  • 最终演示的架构
  • 什么是Prism
  • 什么是Shell
  • 创建Shell和定义区域

第二部分

  • 创建通用基础设施
  • 创建模块
    1. 导航模块
    2. 员工模块
    3. 软件模块
    4. 硬件模块
    5. 请求模块
  • 加载模块
  • 演示应用程序中的问题

第三部分

  • 什么是事件聚合器
  • 事件聚合器的使用
  • 什么是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 与多个发布者和订阅者的一般图示描述。

general pictorial description of EventAggregator

在演示应用程序中,EmployeeViewModel 扮演发布者的角色,RequestViewModel 扮演订阅者的角色。EventAggregator 在 EmployeeViewModel 和 RequestViewModel 模块之间充当调解者。下面的截图展示了 EventAggregator 在演示应用程序中的实现。

general pictorial description of EventAggregator
 

如何使用 EventAggregator

要使用 EventAggregator,需要遵循一些标准步骤。我们将在本节中遵循这些步骤来使用 EventAggregator 以解决该问题。

  1. 首先,我们需要创建一个继承自“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。
     
  2. 发布者和订阅者对象必须使用 DI(依赖注入)容器提供的全局 EventAggregator。因此,这里我们将使用 Unity 容器提供的 EventAggregator,它在 UnityBootstrapper 类中初始化。这是开箱即用的,我们需要查看 UnityBootstrapper 类 ConfigureContainer 方法的代码。以下代码取自 Codeplex 的开源 Prism 库代码

    Configure Container Method

    因此,全局 EventAggregator 的创建已经完成,因为我们的 ITHelpDeskBootstrapper 类继承自 UnityBootstrapper。
     
  3. EmployeeViewModel 将充当发布者,因此我们需要注入 EventAggregator 对象。现在,在 EmployeeViewModel 的构造函数中,我们注入了 IEventAggregator 并在构造函数本身中初始化了一个私有字段 eventAggregator,如下所示。
     public EmployeeViewModel(IEmployeeService employeeService, IEventAggregator  eventAggregator)
     {
           this.eventAggregator = eventAggregator;
           this.employeeService = employeeService;
     }
                     
  4. 需要用更新后的值发布员工 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 将无法帮助我们解决我们遇到的问题。
     
  5. 任何数量的对象都可以订阅发布的事件(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。假设我们收到一个需求,即当用户重新访问任何模块时,其控件都必须刷新。这些控件不应保留旧值。重新访问时,它们必须是空的。

到目前为止,如果我们选择了一个软件模块,然后导航到其他模块,然后再次重新访问软件模块,我们将在控件中看到旧的选定值。下面的截图包含了突出显示我们在目前已开发演示中此行为的步骤。

Issue Without 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 步,我们将看到软件模块的控件已根据给定的新要求刷新,如下所示。

With IActiveAware

同样,我们也可以为硬件模块实现 IActiveAware 接口。

结论

在本文系列的这一部分,我们学习了如何在 Prism 应用程序中使用 EventAggregator 和 IActiveAware。这是文章系列的最后一部分,希望您觉得这个系列很有帮助。欢迎您的评论/建议和提问。谢谢。

© . All rights reserved.