使用Prism Library for WPF编写用户界面
UI布局概念
Shell
通常是主应用程序窗口,它由区域组成。区域主要包含在ContentControl
、ItemControl
和TabControl
中,并且Shell不知道区域
中实现了什么。在区域
里面的是视图,视图是UI特定部分的实现,与应用程序的其他部分分离。
下面几节介绍复合应用程序开发的高级核心概念。
Shell
Shell是包含主要UI内容的应用程序根对象。在Windows Presentation Foundation(WPF)应用程序中,Shell是Window
对象。
Shell扮演着为应用程序提供布局结构的母版页的角色。Shell包含一个或多个命名区域,模块可以在其中指定将要出现的视图。它还可以定义某些顶层UI元素,如背景、主菜单和工具栏。
Shell定义应用程序的整体外观。它可以定义在Shell布局本身中显示和可见的样式和边框,还可以定义应用于插入到Shell中的视图的样式、模板和主题。
通常,Shell是WPF应用程序项目的一部分。包含Shell的程序集可以引用或不引用加载到Shell区域中的包含视图的程序集。
视图(Views)
视图是复合应用程序中UI构造的主要单元。您可以将视图定义为用户控件、页面、数据模板或自定义控件。视图封装了您希望与应用程序的其他部分尽可能分离的UI的一部分。您可以根据封装或功能选择视图中的内容,或者您可以选择将某些内容定义为视图,因为您的应用程序中将有该视图的多个实例。
由于WPF的内容模型,定义视图并不需要特定于Prism库。定义视图最简单的方法是定义一个用户控件。要向UI添加视图,你只需要一种方法来构建它并将其添加到容器中。WPF提供了这样的机制,Prism库增加了定义一个区域可以让视图在运行时动态添加的能力。
组合视图(Composite Views)
支持特定功能的视图可能会变得很复杂。在这种情况下,您可能希望将视图划分为多个子视图,并让父视图通过使用子视图作为一部分来构造自身。应用程序可能在设计时静态地执行此操作,或者它也可能支持让模块在运行时通过所包含的区域来添加子视图。而当您有一个未在单个视图类中完全定义的视图时,您就可以将其称为复合视图。在许多情况下,复合视图负责构建子视图并协调它们之间的交互。您可以通过使用Prism的命令和事件聚合器,设计与其同级视图及其父复合视图之间更松散耦合的子视图。
区域
在Prism库中,通过区域管理器、区域和区域适配器来启用区域。
区域管理器
RegionManager
类负责创建和维护宿主控件的区域集合。RegionManager
使用了一个特定于控件的适配器,它将一个新区域与宿主控件关联起来。下图显示了RegionManager
建立的区域、控件和适配器之间的关系。
RegionManager
可以在代码或XAML中创建区域。RegionManager.RegioName
附加属性用于通过将附加属性应用于宿主控件来在XAML中创建区域。
应用程序可以包含一个或多个RegionManager
实例。并且你可以指定想要在区域中注册的RegionManager
实例。如果您希望在可视化树中移动控件,并且不希望在删除附加的属性值时清除该区域,则此功能非常有用。
RegionManager
提供了一个RegionContext附加属性,允许其区域共享数据。
区域实现
区域是一个实现了IRegion
接口的类。术语 区域 表示一个容器,它可以容纳在UI中显示的动态数据。区域允许Prism库将模块中包含的动态内容放置在UI容器中的预定义占位符中。
区域可以容纳任何类型的UI内容。模块可以包含作为用户控件呈现的UI内容、与数据模板关联的数据类型、自定义控件或这些内容的任意组合。这使得您可以自定义UI区域的外观,然后让模块将内容放置在这些预先确定的区域中。
一个区域可以包含零个或多个项目。根据区域所管理的宿主控件的类型,一个或多个项目可能是可见的。例如ContentControl
只能显示单个对象,但是它所在的区域可以包含许多项目。以及ItemsControl
可以显示多个项目。这允许区域中的每个项目在UI中可见。
在下图中,示例应用程序Shell包含四个区域:MainRegion、MainToolbarRegion、ResearchRegion和ActionRegion。这些区域由应用程序中的各个模块填充,内容可以随时更改。
用户控件模块到区域的映射
要演示模块和内容如何与区域相关联,请参见下图。它显示了WatchModule
和NewsModule
与Shell中相应区域的关联。
MainRegion包含WatchListView
用户控件,该控件被包含在WatchModule
中。ResearchRegion还包含ArticleView
用户控件,该控件被包含在NewsModule
中。
在使用Prism库创建的应用程序中,像这样的映射将成为设计过程的一部分。因为设计人员和开发人员使用它们来确定在特定区域中包含哪些内容。这使设计人员可以确定所需的总体空间以及必须添加的任何其他项目,以确保在允许的空间内可以查看这些内容。
默认区域功能
虽然您不需要完全理解区域实现来使用它们,但了解控件和区域如何关联以及默认区域功能可能会很有用:例如,区域如何定位和实例化视图,视图为活动视图时如何被通知,或者视图生命周期如何与激活相关联。
下面介绍区域适配器和区域行为。
区域适配器
要将UI控件暴露为区域,它必须具有区域适配器。区域适配器负责创建区域并将其与控件相关联。Prism允许您使用IRegion
接口以一致的方式管理UI控件内容。每个区域适配器适配一种特定类型的UI控件。Prism库提供以下三种区域适配器:
-ContentControlRegionAdapter
。此适配器适配类型为System.Windows.Controls.ContentControl
及其派生类的控件。
-SelectorRegionAdapter
。此适配器适配从System.Windows.Controls.Primitives.Selector
类派生的控件,例如System.Windows.Controls.TabControl
控件。
-ItemsControlRegionAdapter
。此适配器适配类型为System.Windows.Controls.ItemsControl
及其派生类的控件。
区域行为
Prism库引入了区域行为的概念。这些是可插入组件,为区域提供了大部分功能。引入区域行为是为了支持视图发现和区域上下文(本主题后面会介绍)。此外,行为还提供了一种扩展区域实现的有效方法。
区域行为是一个附加在区域上的类,用于赋予区域额外的功能。这个行为依附于区域,并在区域的生命周期内保持活跃。例如,当一个AutoPopulateRegionBehavior
附加到一个区域时,它会自动实例化并添加任何以该名称注册的ViewTypes
。在区域的生命周期内,它会持续监控RegionViewRegistry
以获取新的注册信息。无论是在系统范围内还是在每个区域的基础上,添加自定义的区域行为或者替换已有的区域行为都很容易。
下面介绍自动添加到所有区域的默认行为。其中一个只依附于来自Selector
的控件行为SelectorItemsSourceSyncBehavior
。
注册行为
RegionManagerRegistrationBehavior
负责确保区域注册到正确的RegionManager
上。当视图或控件作为另一个控件或区域的子项添加到可视化树时,控件中定义的任何区域都应在父控件的RegionManager
中注册。当删除子控件时,注册的区域也将取消注册。
自动填充行为
有两个类负责实现视图发现。其中之一是AutoPopulateRegionBehavior
类。当它附加到一个区域时,它会检索在该区域名称下注册的所有视图类型。然后它会创建这些视图的实例并将它们添加到区域中。在创建区域后,AutoPopulateRegionBehavior
会监视RegionViewRegistry
,以获取该区域名称的任何新注册的视图类型。
如果您想更好地控制视图发现过程,请考虑创建您自己的IRegionViewRegistry
和AutoPopulateRegionBehavior
的实现。
区域上下文行为
区域上下文功能包含在两个行为中:SyncRegionContextWithHostBehavior
和BindRegionContextToDependencyObjectBehavior
。这些行为负责监视对该区域所做的上下文更改,然后将上下文与附加到视图的上下文依赖属性进行同步。
激活行为
RegionActiveAwareBehavior
负责通知视图是活动的还是非活动的。视图必须实现IActiveAware
来接收这些变化通知。这种主动感知的通知方式是单向的(它从行为传播到视图)。视图无法通过更改IActiveAware
接口上的属性来影响其活动状态。
区域生命周期行为
RegionMemberLifetimeBehavior
负责确定项目在停用时是否应从区域中删除。RegionMemberLifetimeBehavior
监视区域的ActiveViews
集合以发现转变为停用状态的项目。该行为会检查已删除的项的IRegionMemberLifetime
或RegionMemberLifetimeAttribute
(按此顺序)以确定是否应在删除时保持活动状态。
如果集合中的项目是System.Windows.FrameworkElement
,它还将检查其DataContext
是否有IRegionMemberLifetime
或 RegionMemberLifetimeAttribute
。
区域项目按以下顺序检查:
IRegionMemberLifetime.KeepAlive
valueDataContext
'sIRegionMemberLifetime.KeepAlive
valueRegionMemberLifetimeAttribute.KeepAlive
valueDataContext
'sRegionMemberLifetimeAttribute.KeepAlive
value
控件特定行为
SelectorItemsSourceSyncBehavior
仅用于派生自Selector
的控件,例如WPF中的选项卡控件。它负责将区域中的视图与选择器的项目同步,然后将区域中的活动视图与选择器的选定项目同步。
区域实现扩展
Prism库提供了扩展点,允许您自定义或扩展所提供API的默认行为。例如,您可以编写自己的区域适配器、区域行为、或更改导航API解析URI的方式。
视图组合
视图组合就是构建视图。在复合应用程序中,多个模块的视图必须在运行时显示在应用程序UI中的特定位置。要实现这一点,您需要定义视图将出现的位置,以及如何在这些位置中创建和显示视图。
视图可以通过视图发现自动创建并显示在位置上,也可以通过视图注入以编程方式进行创建和显示。这两种技术决定了如何将单个视图映射到应用程序UI中的命名位置上。
视图发现(View Discovery)
在视图发现中,你在RegionViewRegistry
中建立了区域名称和视图类型之间的关系。当区域被创建时,该区域会查找所有与该区域相关联的ViewTypes
,并自动实例化和加载相应的视图。因此在使用视图发现时,你不能明确控制对应于一个区域的视图何时被加载和显示。
视图注入(View Injection)
在视图注入中,您的代码会获取对区域的引用,然后以编程方式向其中添加视图,通常这是在模块初始化时或作为用户操作的结果完成的。您的代码将按名称查询特定区域的RegionManager
,然后将视图注入其中。使用视图注入时,您可以更好地控制何时加载和显示视图。您还可以从该区域中删除视图。但是使用视图注入时,您不能将视图添加到尚未创建的区域。
导航
Prism 8包含导航API。导航API通过允许您导航到一个URI来简化视图注入过程。导航API会实例化视图,并将其添加到区域中,然后激活它。此外导航API允许导航到区域中先前创建的视图。有关导航api的更多信息,请参见基本区域导航。
何时使用视图发现(View Discovery) vs. 视图注入(View Injection)
选择对区域使用哪种视图加载策略取决于应用程序的要求和区域的功能。
在以下情况下使用视图发现:
- 需要或要求自动加载视图。
- 视图的单个实例会被加载到区域中。
在以下情况下使用视图注入:
- 您的应用程序使用导航API。
- 您需要显式或编程控制何时创建和显示视图,或者您需要从区域中删除视图;例如作为应用程序逻辑或导航的结果。
- 您需要在一个区域中显示相同视图的多个实例,其中每个视图实例都绑定到不同的数据。
- 您需要控制将视图添加到区域的某个实例。例如您想要将客户详细信息视图添加到特定的客户详细信息区域。(此方案需要实现范围区域,如本主题后面所述。)