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

深入了解 Prism WPF 应用中的区域创建和区域适配器

starIconstarIconstarIconstarIconstarIcon

5.00/5 (11投票s)

2015 年 12 月 13 日

CPOL

9分钟阅读

viewsIcon

32367

downloadIcon

863

要在基于 Prism 的 WPF 应用程序中有效地处理区域,我们必须理解 WPF 控件、RegionAdapters 和 Regions 之间的关系。在本文中,我们将通过一个演示应用程序了解区域的创建过程以及 RegionAdapters 所扮演的关键角色。

引言

要在基于 Prism 的 WPF 应用程序中有效创建和使用区域,理解 WPF 控件、RegionAdapters 和 Regions 之间的关系至关重要。RegionAdapters 在区域创建过程中发挥着至关重要的作用,并决定其行为。在本文中,我们将学习区域的创建过程以及 RegionAdapters 在其中的作用。我们将使用 WPF 和 Prism 4 创建一个演示应用程序。我们将在其中使用 Prism 框架提供的所有三种 RegionAdapters(ContentControlRegionAdapter、ItemsControlRegionAdapter 和 SelectorRegionAdapter)。

先决条件: 我假设您对 WPF 和 Prism 框架有一定的基本了解。Prism 是一个用于使用 WPF/Silverlight 构建复合应用程序的框架。我已在名为“Prism in WPF - Part One”的文章系列中进行了分步解释和演示。要了解更多关于 Prism 的知识,请参阅 Prism 入门介绍、MSDN 上的 Prism 页面 以及关于 Prism 的 更多信息

目录

  • 区域和 RegionAdapters
  • 区域创建过程
  • WPF 控件和 RegionAdapters
  • 激活和停用视图
  • 显示和隐藏视图扩展
  • 学习总结
  • 演示概述

区域和 RegionAdapters

区域使标准的 WPF 控件(如 Button、ListBox、ComboBox 等)能够像动态的视图容器一样运行,并具有某些其他行为。在运行时,我们可以在区域中添加、删除、激活、停用、显示和隐藏视图。RegionAdapters 在实现这一点方面起着核心作用。区域可以采用来自不同模块的视图,我们将在创建演示应用程序时这样做。

与区域相关的两个主要概念是:RegionManager 和 RegionAdapters。RegionManager 负责为宿主 WPF 控件创建和维护区域集合。RegionAdapters 负责创建区域并将其与宿主 WPF 控件关联。在典型的 Prism 应用程序中,我们通常需要理解并选择不同的 WPF 控件来承载区域。通过选择特定的 WPF 宿主控件,我们**间接选择了特定的 RegionAdapter 和 Region**。本文将解释这些隐式选择,并帮助您在创建区域时做出更好的决策。

有时我们还需要创建自定义的 RegionAdapters 和 Regions。在 Prism 库中,开箱即用提供了三种 RegionAdapters 和相应的 Regions。我们将在后面详细介绍它们。下一节将探讨区域创建过程。

区域创建过程

在 XAML 中,我们可以使用以下代码创建一个区域

 <ListBox Name="MainRegion" prism:RegionManager.RegionName="MainRegion"/>

在上面的代码中,我们使用了一个标准的 WPF ListBox 控件,并设置了 RegionManager 的一个附加属性 RegionName。RegionManager 类定义了附加属性,如下面的 Prism 库源代码所示。关注 DependencyProperty.RegisterAttached 方法,它用于创建附加属性(而不是仅创建依赖属性的 Register 方法)。

public static readonly DependencyProperty RegionNameProperty = DependencyProperty.RegisterAttached(
            "RegionName",
            typeof(string),
            typeof(RegionManager),
            new PropertyMetadata(OnSetRegionNameCallback));

RegionManager 会根据特定的 WPF 控件和 RegionAdapters 之间的映射关系,根据指定的映射创建 RegionAdapter。这种映射定义在 "Bootstrapper.cs" 类中,如下所示。它表示如果 WPF 控件的类型是 Selector、ItemsControl 或 ContentControl,那么 RegionManager 将分别创建 SelectorRegionAdapter、ItemsControlRegionAdapter 或 ContentControlRegionAdapter。下一节我们将详细介绍 WPF 控件和 RegionAdapters 之间的关系。

protected virtual RegionAdapterMappings ConfigureRegionAdapterMappings()
        {
            RegionAdapterMappings regionAdapterMappings = ServiceLocator.Current.GetInstance<RegionAdapterMappings>();
            if (regionAdapterMappings != null)
            {
                regionAdapterMappings.RegisterMapping(typeof(Selector), ServiceLocator.Current.GetInstance<SelectorRegionAdapter>());
                regionAdapterMappings.RegisterMapping(typeof(ItemsControl), ServiceLocator.Current.GetInstance<ItemsControlRegionAdapter>());
                regionAdapterMappings.RegisterMapping(typeof(ContentControl), ServiceLocator.Current.GetInstance<ContentControlRegionAdapter>());
            }
            return regionAdapterMappings;
        }

最后,RegionAdapter 会为您创建一个特定类型的区域。默认情况下,ContentControlRegionAdapter、ItemsControlRegionAdapter 和 SelectorRegionAdapter 分别创建 SingleActiveRegion、AllActiveRegion 和 Region 类 的实例。下图是区域如何被创建并与 WPF 控件关联的图示。

Install Prism Framework
 

WPF 控件和 RegionAdapters

正如我们在上一节中讨论的,根据 WPF 控件和 RegionAdapters 之间的映射,RegionManager 会创建 RegionAdapters。Prism 库提供了以下三种 RegionAdapters:

  1. ContentControlRegionAdapter: 此适配器用于适配 System.Windows.Controls.ContentControl 及其派生类。
  2. ItemsControlRegionAdapter: 此适配器用于适配 System.Windows.Controls.ItemsControl 及其派生类。
  3. SelectorRegionAdapter: 此适配器用于适配 System.Windows.Controls.Primitives.Selector 的派生类。

在 XAML 中定义区域时,**选择合适的 WPF 控件至关重要**,以便创建合适的 RegionAdapter 和 Region 来满足我们的需求。我们可以提供新的映射并创建自定义的 RegionAdapters,但首先需要选择合适的 WPF 控件来创建区域。下图展示了可用于创建区域的控件之间的关系。

Relationship between WPF Controls

根据上图,ContentControl 和 ItemsControl 在层次结构上是并行的,它们都继承自 Control 并实现了 IAddChild 接口。ContentControl 用于承载单个项或视图。

但 ItemsControl 实现了一个名为 IGeneratorHost 的附加接口,以支持 ItemContainers 的行为,因为它需要多个项。 IGeneratorHost 是 ItemContainerGenerator 与其宿主通信的接口。

最后,Selector 继承自 ItemsControl,并提供了在多个托管项中选择一项的附加功能。**Selector 是一个抽象类,必须使用继承自 Selector 的具体类**。要查找哪些控件可以用作 Selector,我们可以访问 MSDN 上的 Selector 文档。如该页面所示,我们可以使用 ComboBox、ListBox 或 TabControl 等作为 Selector。

Selector Class

类似地,我们可以在 MSDN 上的 ContentControl 文档ItemsControl 文档 中找到继承自 ContentControl 和 ItemsControl 的控件。在 ContentControl 文档 中,我们可以看到**即使是 Label 和 Button(ButtonBase 的派生类)也可以用来创建区域**。在演示代码中,您可以注释掉现有的创建区域的代码,然后取消注释另一个使用不同控件的类似代码来验证这一点。

激活和停用视图

激活和停用用于将视图标记为活动和非活动状态。为了提高应用程序的效率,我们可能需要根据不同场景来停用和激活视图。如果视图实现了 IActiveAware 接口,我们可以定义激活或停用时执行的操作。在 本文 中对 IActiveAware 接口进行了实时示例的解释。只有两种 RegionAdapters 支持激活和停用:ContentControlRegionAdapter 和 SelectorRegionAdapter。

由于 ContentControlRegionAdapter 只包含一个视图,因此如果您停用当前视图,区域将为空;如果您激活一个新的视图,新视图将成为区域的内容,而之前的视图将被停用。

在 SelectorRegionAdapter 的情况下,您可以激活和停用视图。但是,使用 SelectorRegionAdapter 创建的区域会显示活动和非活动的视图。

ItemsControlRegionAdapter 不支持停用,尝试停用将引发 InvalidOperationException。如果您尝试在 ItemsControlRegionAdapter 创建的区域内停用视图,将收到一个 InvalidOperationException 异常,显示:“在 Microsoft.Practices.Prism.dll 中发生未处理的 System.InvalidOperationException 类型的异常”,附加信息为:在此类区域中无法停用。

显示和隐藏视图扩展

正如我们在上一节讨论的,SelectorRegionAdapter 会显示活动和非活动的视图,而 ItemsControlRegionAdapter 完全不支持停用。在这些情况下,如果我们想要隐藏/显示视图,Prism 并不提供此功能。因此,我们需要编写代码来实现此功能。Matias Bonaventura 有一篇很棒的 博客文章,其中包含具有隐藏和显示功能的 RegionExtensions 类的代码。我们在演示应用程序中使用了该类来提供显示和隐藏功能。

ContentControlRegionAdapter 只支持单个视图,因此激活和显示的操作方式相同。

学习总结

下表总结了到目前为止使用的控件、RegionAdapters 和其他相关概念。

用于创建区域的 WPF 控件 ContentControl 或其派生控件 ItemControl 或其派生控件 Selector 的派生控件
控件的通用用途 表示一个具有任何类型单个内容的控件 表示一个可用于显示项目集合的控件 表示一个允许用户从其子元素中选择项目的控件
使用的 RegionAdapter 类 ContentControlRegionAdapter ItemsControlRegionAdapter SelectorRegionAdapter
使用的 Region 类 SingleActiveRegion AllActiveRegion 区域
活动视图的数量 单个视图(始终活动且可见),如果您对该单个视图执行停用/隐藏操作,ContentControl 将变为空 多个视图(全部活动),执行停用会引发异常,但您可以隐藏视图 多个视图,活动视图和非活动视图都可见。您需要调用隐藏/显示来设置活动/非活动视图的可见性。
停用视图 会起作用,但由于是对单个内容视图调用停用,因此区域将变为空 将引发 InvalidOperationException 异常,显示:“在 Microsoft.Practices.Prism.dll 中发生未处理的 System.InvalidOperationException 类型的异常”,附加信息为:在此类区域中无法停用。 会起作用
活动与可见 活动视图将是可见的,因为它被设置为 ContentControl 的内容(因为只有一个视图) 所有视图(默认情况下活动)都可见。(直到我们编写了扩展来隐藏) 所有视图(活动和非活动)都可见
隐藏与停用 由于只支持单个视图,因此活动视图无法隐藏,非活动视图也无法显示。 隐藏可用,但停用会引发异常 显式调用隐藏,因为它是活动/非活动视图。默认情况下,即使是非活动视图也会显示。

演示概述

我们将简要介绍演示应用程序和一些关键步骤,以及如何进行实验。我希望演示中的大部分代码都是自 explanatory 的。如果您需要任何解释,请随时发表评论。

演示解决方案包含三个项目

  1. RegionAdaptersDemoApp: 使用标准的 WPF 项目模板创建。它包含 Shell(MainWindow),其中定义了区域。在区域中,我们使用了其他两个模块项目(ModuleA 和 ModuleB)中定义的视图。
  2. RegionAdaptersDemoApp.Module A: 使用类库项目模板创建。代表模块 A,包含两个视图:View1 和 View2。
  3. RegionAdaptersDemoApp.ModuleB: 使用类库项目模板创建。代表模块 B,包含两个视图:View3 和 View4。

我们正在使用 Prism 4 和目标为 .NET Framework 4 的 WPF 项目,以便演示应用程序可以在 Visual Studio 2010 及更高版本上运行,无需额外设置。创建项目后,我们应该通过在 Nuget 包管理器控制台中运行以下命令来安装“Prism.UnityExtensions”nuget 包。

    Get-Project -All | Install-Package Prism.UnityExtensions -Version 4.0.0

下图显示了 Nuget 包管理器控制台。

Installing Prism 4

在 ModuleA 中,添加视图时,请添加 WPF 用户控件(称为“View1”和“View2”),如下所示。

Adding WPF User Control

如果尚未包含,请为“System.XAML”添加引用(因为它使用的是类库项目模板)。ModuleB 与 ModuleA 非常相似。

在 RegionAdaptersDemoApp 项目中,查看 MainWindow.xaml。目前我们使用 ListBox 创建了区域。并且有注释掉的代码,可用于**尝试使用其他 WPF 控件**来创建区域。您可以使用名为“View Region Creation Detail”的按钮来查看使用该 WPF 控件创建区域时所使用的类的详细信息。

在控件部分,您可以选择视图,执行添加/删除/激活/停用/显示/隐藏操作,**前提是当前场景适用**,否则将引发异常。

最终的演示应用程序外观如下:

Adding WPF User Control
 

结论

在本文中,我们学习了 WPF 控件、RegionAdapters 和 Regions 之间的关系。希望这能帮助您在创建基于 Prism 的 WPF 应用程序中的区域时做出明智的决策。非常欢迎您的评论/建议和疑问。谢谢。

© . All rights reserved.