Prism 4 文档 ---第7章 组成用户界面

     一个应用程序的用户界面(UI)能够通用如下几种模式之一来构建:html

窗体所须要全部的控件都包含在一个单独的XAML文件中,在设计时组合这个窗体。web

窗体的逻辑区域被分割到单独的部分中,一般指哟过户控件。这些部分被窗体引用而且在设计时组合窗体。shell

窗体的逻辑区域被分割到单独的部分中,一般指哟过户控件。这些部分是不被窗体所知道的,而且在运行时动态的将它们添加到窗体中。使用这种方法的应用程序称为复合应用程序。数据库

    一个复合应用程序的UI是从被认为是一般包含在程序模块中的View的可视化组件松耦合的组合起来的,可是它们不须要是。若是你将应用程序切分红模块,你须要某种方式来松散的组合UI,可是你可能甚至在View不在模块中时也选择使用这种方式。为了用户,该应用程序提供了一个无缝的用户体验,并提供了一个彻底集成的应用程序。express

    为了组合你的UI,你须要一个容许你建立一个能松散耦合在运行时建立可视化元素的架构。另外,这个架构应该体统这些元素以一种松散耦合方式通讯的策略。编程

    Stock Trader RI 就是经过加载多个来自不一样模块中的经过Shell暴露给Region的View来组合起来的,以下图所示。windows

clipboard

UI布局概念设计模式

    在一个组合应用程序中的根对象被认为是Shell。Shell在应用程序中扮演这一个母版页的角色。Shell包含了一个或者多个区域。区域是在运行时会被加载的内容的占位符。区域被附加到UI元素上,例如内容空,列表项控件,Tab控件或者一个自定义控件而且管理UI元素的内容。区域的内容能够被自动的加载或者按须要加载,这取决于应用程序的需求。性能优化

    一般,一个区域的内容是一个View。一个View封装了UI的你想要尽量与程序的其余部分保持松耦合关系的一部分,你能够将一个View定义成一个用户控件,数据模板,捉着甚至是一个自定义控件。网络

    一个区域管理着View的显示和布局。区域能够经过它们的名称以一种松耦合的方式被访问而且支持动态的添加和移除Views,一个区域附加的一个宿主控件,将区域想象成一个动态加载View的容器。

    接下来的几节将会介绍复合应用程序开发中的高级核心概念。

Shell

    shell是应用程序中一个包含着主要UI内容的跟对象。在WPF应用程序中,shell是一个Window对象,在Silverlight程序中,shell是RootVisualUserControl

    shell扮演着提供应用程序布局结构的母版页的角色。shell包含着一个或者多个命名的区域,这些区域能够指定将会显示的View。它也能够定义具体的顶级的UI元素,例如背景,主菜单和工具条。

    shell订阅了应用程序的总体外观。它可能会定义在它的布局中展示的可见的样式和边框。它也可能定义了将会应用到添加到shell中的View的样式,模板和主题等。

    一般,shell是WPF应用程序项目的一部分或者Silverlight项目的主要内容。包含shell的程序集也许会也或许不会依赖包含着将会被加载到shell的区域内的Views的程序集。

Views

    View是复合应用程序中UI结构中主要的单元。你能够将view定义成一个用户控件,数据模板,或者一个自定义控件。一个View封装了UI的你想要尽量与程序的其余部分保持松耦合关系的一部分。你能够选择基于封装或者一块功能的View中发生的内容,或者你能够选择定义一些东西做为一个view由于你将会在应用程序中产生这个View的多个示例。由于WPF和Silverlight的内容模型,Prism类库定义一个view时不须要什么指定的东西。最贱的订阅一个view的方法就是定义成一个用户控件。为了往UI中添加一个view,你只须要一种方法去构造它并将它添加到容器中。WPF和Silverlight提供了实现这些内容的机制。Prism类库添加了定义了能够在运行时动态的添加到一个区域的能力。

组合Views

    支持特定功能的视图可能会变得复杂。在这种状况下,你可能要将View拆分红几个子View,并由父View处理利用子View来构建自身。应用程序可能在设计时静态的配置这些,或者它可能支持在运行时经过一个包含的区域将子View添加到模块中。当你有一个在某个类中未能完整的定义View的类时,你能够选用一个组合View。在许多的状况下,一个组合view扶着这构建子view及协调tam之间的交互。你能够经过使用Prism类库命令和事件聚合组合来定义与其余子view以及它们的父view之间松耦合的子view。
Views和设计模式

    虽然Prism类库并无要求你使用它们,可是,你在实现一个view时考虑使用一种UI设计模式。Stock Trader RI和QuickStarts演示了使用MVVM做为实现一个View布局和View逻辑之间清晰的分离的模式。

    推荐使用MVVM UI设计模式由于它原生的适应于XAML平台,WPF,Silverlight,以及Silverlight for Windows Phone 7。系统的依赖属性和平台丰富的数据绑定支持使得View和ViewModel之间可以以一种松耦合分方式进行通讯。

    view和逻辑的分离对于可测试性与可维护性很是重要,它提高了开发与设计工做流的效率。

    你可使用一个用户控件或者一个自定义控件而且将全部的逻辑卸载后台代码文件中来建立一个View,你的View将会很是难测试由于你必须在单元测试的逻辑中建立一个View的实例。这是一个问题,特别若是view源自,或依赖于运行的WPF或Silverlight组件做为其执行上下文的一部分。为了保证你能够在单元测试中在一个不依赖这些的隔离的环境中测试view逻辑。你须要可以建立视图的模拟,以消除执行上下文,这须要将view和逻辑拆分到单独的类中。

    若是你将一个view做为一个数据模板,这将会使得它自身没有相关的代码。所以,你须要将这些相关的逻辑放到其余的地方。一样布局与逻辑清晰分离也是可测试性所须要的也有助于使view更易于维护。

    注意:

    单元测试和UI自动测试是两种不一样测试覆盖的测试类型。

    单元测试的最佳实践建议的对象单独进行测试。为了达到目的隔离,你须要一个样机或存根每一个外部依赖。而后,单元测试颗粒能够运行侵害测试对象。

    UI自动化测试运行该应用程序,适用于用户界面的手势,而后测试预期的结果。这种类型的测试验证UI元素已正确链接到应用程序逻辑。

    view与逻辑的分离提供了关注点的清晰分离。另外对于可测试性的考虑,这种分离使得UI设计者与开发者相互独立。更多的关于MVVM模式,请看第五章“实现MVVM模式”。

命令,UI触发器,动做和行为

    当在后台文件中实现了view的逻辑,你能够将UI交互服务添加到事件上。可是,当使用MVVM时,view model不能直接的处理UI引起的事件。为了在viewmodel中路由处理这些事件动做,你可使用命令,UI触发器,动做,以及行为。

命令

    命令分开了调用从逻辑中执行命令的逻辑对象和语义。内建的命令代表了一个动做是否可用的能力。UI中的命令绑定到了ViewModel中的ICommand属性。关于命令的更多信息。请看第5章“实现MVVM模式”中的“命令”一节。

UI触发器,动做和行为

    触发器,动做和行为是Microsoft.Expression.Interactivity 命名空间下的一部分而且同Expression Blend一块儿发布。而且它们也是Expression SDK的一部分。触发器,动做和行为提供了一个处理UI事件或者命令的全面的API,而且经过路由将它们绑定到DataContext暴露的ICommand属性提供的方法。有关 UI触发器,动做和行为的更多信息,请看第5章“实现MVVM模式”中“从View调用命令对象”和“从View调用命令方法”这两节。

数据绑定

    数据绑定是XAML平台的框架中最重要的功能之一。为了可以在XAML平台上成功的开发应用程序。你须要对数据绑定有一个深入的理解。

    数据绑定拥有依赖属性系统提供给的固有的属性变化通知机制的所有有点。当结合了CLR中INotifyPropertyChanged接口的实现时,在数据绑定中变动通知能够作到目标与源对象之间无代码的交互。

    数据绑定经过使用一个值转换从一个类型转换到另外一个类型从而可使得不一样目标和源类型之间的绑定。数据绑定在它的管道中保留有多重校验的钩子,你能够在用户输入时进行数据验证。

    强烈的鼓励你去读一读MSDN上的"Dependency Properties Overview" and "Data Binding Overview"。这两个主题的彻底理解对于在XAML平台上开发应用程序是很是关键的。有关数据绑定的更多信息,请看第5章”实现MVVM模式“的”数据绑定“一节。

区域(Regions)

    在Prism类库中区域贯穿着区域管理,区域,区域适配器,接下来的几节将会介绍它们是如何在一块儿工做的。

区域管理

    RegionManager类负责建立和维护承载控件的区域的集合。RegionManager使用了一个控件指定适配器来关联区域和承载控件。下面的插图显示了由RegionManager类建立的区域,控件和适配器之间的关系。

clipboard[1]

RegionManager类可在代码或者XAML中建立区域。RegionManager.RegionName附加属性用来应用到承载控件的附加属性中在XAML中建立区域。

    应用程序能够包含一个或者多个RegionManager类的示例。你能够指定你本身的区域要注册到的RegionManager类的实例。这在你想要在可视化书中移动控件而且不想在附加属性值被删除后再次声明区域时很是有用。

RegionManager类提供了一个RegionContext附加数据来使得区域之间共享数据。

区域实现

    区域是一个实现了IRegion接口的类。Region术语表明了一个能够拥有在UI上呈现的动态数据的容器。区域使得Prism类库在UI容器中经过预约义占位符来的放置模块中的动态内容。

    区域能够承载任何类型的UI内容。模块中包含的UI内容能够呈现为一个用户控件,一个关联了数据模板的数据类型,一个自定义控件,或者它们的组合。这使得你能够定义UI区域的内容,而后将模块中的内容放置到UI区域中。

    一个区域能够包含零个或多个子项。这取决于区域管理的承载控件的类型,能够显示一个或者多个子项。例如,ContentControl控件只能展现一个对象,然而,加载的区域能够包含多个子项,而且,ItemsControl能够展现多个子项。容许每个都在UI中是可见的。

    在下面的插图中,Stock Trader RI Shell包含了四个区域:MainRegion, MainToolbarRegion, ResearchRegion, 和ActionRegion.这些区域是被不一样的模块发布的,而且这些内容能够随时被改变。

clipboard[2]

用户控件模块到区域映射

    为了演示模块和内容是如何跟区域关联在一块儿的,状况下面的插图,它展现了WatchModuleNewsModule 在Shell中对于区域的关联关系。

MainRegion 包含了WatchModule中的 WatchListView 控件,ResearchRegion 包含了NewsModule中的 ArticleView 控件 。

    在基于Prism窗口的应用程序中,这样的映射关系将会是设计过程的一部分,由于设计人员和开发人员使用它们来决定将内容呈现到指定的区域中。这使得设计人员来决定须要的所有空间以及确保在可用控件中展现额外项目的空间。

clipboard[3]

默认功能性区域

    当你须要彻底的理解区域实现来使用它们的时候,理解控件和区域之间是如何被关联的以及默认区域的功能将会颇有用。例如,如何定位区域和实例化View,如何在View激活时被通知,或者view的生命周期被激活。

区域适配器

    为了将UI控件暴露为一个区域,它须要一个区域适配器。区域适配器扶着建立一个区域和关联控件。这容许你使用IRegion接口经过统一的方式管理UI控件中的内容。每个区域适配器适配一个指定的UI控件。Prism类库提供了如下三个区域适配器:

  • ContentControlRegionAdapter。这个适配器适配System.Windows.Controls.ContentControl控件及其派生控件。
  • SelectorRegionAdapter。这个适配器适配System.Windows.Controls.Primitives.Selector的派生控件,例如System.Windows.Controls.TabControl控件。
  • ItemsControlRegionAdapter。这个适配器适配System.Windows.Controls.ItemsControl控件及其派生控件。

    注意:

Silverlight版本的Prism中支持名为TabControlRegionAdapter的第四个适配器,这是由于SilverLight中的TabControl控件没有继承Selector类,而且与WPF版本的控件行为不相同。

区域行为

    Prism类库介绍了区域行为的概念,这里有一些区域的多数的功能性的可插拔的组件,区域行为的引入被用来支持View发现以及区域上下文,并建立一个在WPF和Silverlight中一致的API。另外,行为提供了一种扩展区域实现的高效的方式。

    区域行为是一个附加到区域中使得区域拥有额外功能的类。这种行为被附加到区域中而且在区域的生命周期中一直保持活动状态。例如,当AutoPopulateRegionBehavior附加到一个区域,它将会自动实例化,而且添加添加任何以名称注册到区域中的ViewTypes。在区域的声明周期中,它一直监视RegionViewRegistry用以新的注册,添加或替换一个已存在的自定义的区域行为是很是容易的,不管是在一个系统层面仍是一个单独的基于区域的。

    接下来的几节描述自动添加到全部区域中的默认行为。惟一一个附加到派生自Selector的控件行为是,SelectorItemsSourceSyncBehavior。

注册行为

RegionManagerRegistrationBehavior负责确保区域添加到正确的RegionManager中。当一个view或一个控件做为另外一个控件或者区域的子项添加到可视树中时,这个控件中定义的任何区域都应该被注册到父控件的RegionManager中,当子控件被移除后,这些注册的区域将会被卸载。

自动填充行为

    有两个类负责View发现功能的实现。一个是AutoPopulateRegionBehavior。当它被添加到一个区域时,它将会检索区域名称下全部注册的view类型。而后,它建立这些view的实例并将他们添加到区域中。在区域被建立以后,AutoPopulateRegionBehavior监视RegionViewRegistry用以区域名称中任何新注册的view类型。

    若是你想要在view发现过程当中处理更过控件,考虑建立你本身的关于IRegionViewRegistryAutoPopulateRegionBehavior的实现。

区域上下文行为

    区域上下文的功能被包含在两个行为中:SyncRegionContextWithHostBehavior BindRegionContextToDependencyObjectBehavior。这些行为负责监视上下文的区域发生的变化以及经过附加到view的上下文依赖属性来同步上下文信息。

激活行为

RegionActiveAwareBehavior 负责当view在激活或者非激活时通知view,view必须实现IActiveAware接口来接受这些变化的通知。激活通知是单向的(它从行为传递到view)。view不能经过改变IActiveAware接口的激活属性来影响它的活动状态。

区域生命周期行为

RegionMemberLifetimeBehavior负责着决定某项当它再也不是活动的时候是否应该从区域中移除。RegionMemberLifetimeBehavior
指定控件的行为监视着ActiveViews集合来发现那些转变为非活动状态的项目。这个行为检查IRegionMemberLifetimeRegionMemberLifetimeAttribute(按该顺序)移除的项目,已决定它是否应该在移除后保持生命周期。

如何集合中的项目System.Windows.FrameworkElement类型的对象,它也会检查她的IRegionMemberLifetimeRegionMemberLifetimeAttribute.的DataContext属性。

这些区域项按一下顺序被检查:

  1. IRegionMemberLifetime.KeepAlive
  2. DataContext's IRegionMemberLifetime.KeepAlive
  3. RegionMemberLifetimeAttribute.KeepAlive
  4. DataContext's RegionMemberLifetimeAttribute.KeepAlive

控件指定的行为

    SelectorItemsSourceSyncBehavior只用于那些派生自Selector的控件,好比WPF中的Tab控件。他负责区域中的Views和选择容器中项目之间的同步,而且负责区域中活动的Views和选中项之间的同步。

    TabControlRegionSyncBehavior只用于Silverlight,它为Siliverlight中的Tab控件提供了与SelectorItemsSourceSyncBehavior相同的行为。

扩展区域实现

    Prism类库提供了容许你去自定义或者扩展API提供的默认行为的扩展点。例如,你能够开发你本身的区域适配器,区域行为,改变API转换URI导航的方式,或者在Silverlight Frame Navigation中扩展API的导航工做。关于扩展Prism类库的扩展相关的内容,请看“扩展Prism”。

View 组合

    view 组合是view的构建工做。在一个组合应用程序中,来自多个模块中中的views须要在运行时在应用程序UI中指定的地方展现。为了达到这个目的,你须要定义这些view将会显示的位置,以及这些view将会如何被建立及如何在这些位置展现。

    View能够经过视图发现自动的方式或经过view注入以编程的方式建立和在指定位置展现。这两种技术肯定了Views如何单独映射到该应用程序的用户界面中指定的位置。

View 发现

    在视图发现中,你在区域的名称和view的类型之间创建RegionViewRegistry关系,当一个区域被建立时,这个区域将会查找与此区域相关联的ViewTypes而且自动的实例化它们和加载相应的views。所以,在视图发现过程当中,你没必要显示的控制,对应的View将会被加载以及展现。

View 注入

    在视图注入中,你须要以编码的方式得到一个区域的引用,而后经过程序的方式将view添加到区域中。一般,这些在一个模块初始化时或者在做为一个用户动做的结果时进行。你将会在代码中经过名称查询一个指定区域的RegionManager,而后将view注入到其中。在视图注入中,你须要在views被加载和展现时更过的手动控制。你也拥有从区域中移除这些view的能力。然而,在视图注入中,你不能将一个view添加到一个尚未被建立的区域中。

导航

    Prism 4.0的类库中包含了导航相关的API。这些导航API经过使你用URI导航到一个区域的方式简化了视图注入的过程。这些导航API实例化了View,添加它到区域中,而后激活它。另外,这些导航API容许返回到区域中建立上一个view。关于导航API的更过信息,请看第8章"导航"

什么时候使用View发现 VS View 注入

    为区域选择使用哪一种视图加载策略取决于应用程序的需求以及区域的功能。

在如下状况下使用视图发现方式:

  • 指望或者要求自动的加载view
  • view的单例将会被加载到区域中。

在如下状况下使用视图注入的方式:

  • 你的应用程序使用了导航API
  • 你须要明确或者经过编程方式控制一个view在建立和展现的时候,或者你须要工一个区域中移除一个view;例如,做为应用程序逻辑或者导航的结果。
  • 你须要展现一个区域中同一个view的多个实例,这些不一样的实例绑定不一样的数据。
  • 你须要控制将view添加到哪个区域实例中。例如,你想要将一个客户详细信息视图添加到一个特定的客户详细区域。

UI布局场景

    在组合应用程序中,来自多个模块中的视图在运行时在应用程序UI中指定的位置被展现。为了达到这点,你须要定义这些视图将会被展现的位置以及这些视图将会如何被建立及在这些位置展现。

    视图和UI中的的位置之间的解耦使得应用程序的界面和布局发展为在区域中显示的独立的视图。

    接下来的几描述了在你开发一个组合应用程序中将会遇到的核心场景。在适当的时候,Stock Trader RI系统的例子将会演示这种场景的解决方案。

实现Shell

    Shell 是应用程序的主界面中包含的根对象,在一个WPF程序中。Shell是一个Window对象。在一个Silverlight程序中,shell是一个RootVisualUserControl。

shell能够包含已命名的区域,模块能够指定视图显示到这些区域中。它也能够定义一个肯定的顶级的UI元素,例如主菜单和工具栏。shell定义了应用程序的整个框架和界面,这个ASP.NET的主页面控件有些类似。它能够定义在自身布局中展现的样式和边框,而且,它也能够定义添加到shell中的视图能够应用的样式,模板,和主题等。

    你不需用使用一个不一样的shell做为应用程序界面的一部分来使用Prism类库。若是你正在构建一个完整的组合应用程序,实现一个提供了良好定义的跟shell而且实现创建应用程序主UI的模式。然而,若是你在添加Prism类库的功能到一个已存在的应用程序,你不要必须改变应用程序的基础结构来添加一个shell。而是,你能够修改已存在的窗口或者控件的定义来添加能够引入视图的区域。

    你也能够在应用程序中存在不止一个应用shell。若是你将会=设计为用户打开多余一个的顶级窗口的应用程序,每个顶级的窗口扮演着包含内容的shell。

Stock Trader RI Shell

WPF Stock Trader RI 使用一个shell做为它的主窗口。在下面的插图中,shell和视图被高亮显示。shell是WPF Stock Trader RI程序启动时的主窗口,它包含了全部的视图。它定义了模块添加它们的视图到的区域和一些列的顶级的UI项,包括CFI Stock Trader title 和 Watch List tear-off 标题。

clipboard[4]

    Stock Trader RI中的Shell.xaml,它的后置代码文件Shell.xaml.cs和它的视图墨香ShellViewModel.cs提供了shell的实现。Shell.xaml文件包含了shell的布局和部分UI元素,包括添加了模块的视图的区域的定义。

    下面的XAML展现了Shell结构以及主要的XAML元素。注意这个RegionName附加属性被用来定义四个区域以及窗体背景图片提供了shell的背景。

<Window x:Class="StockTraderRI.Shell">

 <!—shell background -->
 <Window.Background>
  <ImageBrush ImageSource="Resources/background.png" Stretch="UniformToFill"/>
 </Window.Background>

 <Grid>

   <!-- logo -->
   <Canvas x:Name="Logo">
    <TextBlock Text="CFI" ... />
    <TextBlock Text="STOCKTRADER" .../>
   </Canvas>

   <!-- main bar -->
   <ItemsControl 
    x:Name="MainToolbar" 
    cal:RegionManager.RegionName="{x:Static inf:RegionNames.MainToolBarRegion}">
   </ItemsControl>

   <!-- content -->
   <Grid>
    <Controls:AnimatedTabControl
     x:Name="PositionBuySellTab"
     cal:RegionManager.RegionName="{x:Static inf:RegionNames.MainRegion}"/>
   </Grid>

   <!-- details -->
   <Grid>
    <ContentControl 
     x:Name="ActionContent" 
     cal:RegionManager.RegionName="{x:Static inf:RegionNames.ActionRegion}">
    </ContentControl>
   </Grid>

   <!-- sidebar -->
   <Grid x:Name="SideGrid">
    <Controls:ResearchControl 
     cal:RegionManager.RegionName="{x:Static inf:RegionNames.ResearchRegion}">
    </Controls:ResearchControl>
   </Grid>

 </Grid>
</Window>

    Shell的后置代码实现很是简单。Shell被导出,目的是当启动引导程序建立它时,它的依赖将会被MEF解析。shell的惟一依赖---ShellViewModel--在构造中被注入,以下所示。

C# Shell.xaml.cs
[Export]
public partial class Shell : Window
{
 public Shell()
 {
  InitializeComponent();
 }

 [Import]
 ShellViewModel ViewModel
 {
  set
  {
   this.DataContext = value;
  }
 }
}

    在后置代码文件中的少许的代码和视图模型说明了组合应用程序结构的能力和简单性。这种缺乏代码也说明了在shell和组成它的视图之间的解耦。定义区域

定义区域

 

你经过一个命名的位置定义视图将会显示的布局,叫作区域。区域扮演为一个用于在运行时展现一个或多个视图的占位符。模块能够定位和添加内容到布局中的区域而不须要知道区域是如何以及在哪里展现的。这使得布局的变化将不会影响将内容添加到布局的模块。

    区域经过为WPF或Silverlight中控件分配一个区域名称来,要么在前面展现的Shell.xaml文件中XAML中或者在代码中来定义。区域能够经过它们的名称来被访问。在运行时,视图被添加到命名的区域控件中,这些区域控件而后根据视图的实现的布局策略来显示这些视图。例如。一个Tab控件区域将会将它的子视图在一个分页排列中布局展现。区域支持添加或者移除视图。视图能够经过代码或者自动的被建立和展现在区域中。在Prism类库中,前面的实现方式是经过视图注入,后面的方式是经过视图发现。这两种技术肯定视图如何单独映射到该应用程序的用户界面内,在指定的区域。

    应用程序的Shell定义了最高级别的应用程序的布局;例如。经过指定主要内容和导航内容的位置,就像下面展现的。这些高级别视图中布局相似地被定义,从而容许总体的UI被递归组合。

clipboard[5]

    某些时候区域用于定义多个逻辑相关的视图的位置。这种区域控件一般在派生自ItemsControl控件经过根据它的实现策略来展现视图。例如在Stack或者Tabed布局排列。

    区域也能够用于定义一个单一视图的位置;例如,经过使用一个ContentControl。在这种场景中,区域控件只能在同一时刻展现一个视图,即便有多个视图映射到了这个区域位置。

Stock Trader RI Shell Regions

    Stock Trader RI展现了单一视图和多个视图布局方式,你能够在程序的shell中看到这两种方式,它们定义了应用程序的高级别的视图的位置。下面的插图展现了Stock Trader RI中shell定义的区域。

clipboard[6]

Stock Trader RI中在购买或者销售股票时也展现了多视图布局。购买/出售区域是一个集合样式区域展现了集合中多个购买/出售视图(OrderCompositeView)的一部分,以下所示

clipboard[7]

    shell的ActionRegion包含OrdersView,OrdersView包含Submit All and Cancel All 按钮和OrdersRegionOrdersRegion附加到了显示多个OrderCompositeViewsListBox控件。

IRegion

一个区域是一个实现了IRegion接口的类,区域是承载了经过一个控件显示的内容的容器。下面的代码展现了IRegion接口。

public interface IRegion : INavigateAsync, INotifyPropertyChanged
{
  IViewsCollection Views { get; }
  IViewsCollection ActiveViews { get; }
  object Context { get; set; }
  string Name { get; set; }
  Comparison<object> SortComparison { get; set; }
  IRegionManager Add(object view);
  IRegionManager Add(object view, string viewName);
  IRegionManager Add(object view, string viewName, bool createRegionManagerScope);
  void Remove(object view);
  void Deactivate(object view);
  object GetView(string viewName);
  IRegionManager RegionManager { get; set; }
  IRegionBehaviorCollection Behaviors { get; }
  IRegionNavigationService NavigationService { get; set; }
}

在XAML中添加一个区域

RegionManager 应用了一个在XAML中简化的建立区域的附加属性。为了使用这个附加属性,你必须在XAML中引入Prism类库的命名空间憨厚使用RegionName附加属性。下面的实例展现了如何在一个AnimatedTabControl的窗口中使用这个附加属性。这个实例在XAML中消除了魔法字符串。

    注意使用X:Static扩展标记来引用MainRegion字符内容。

XAML (WPF)
<Controls:AnimatedTabControl
  x:Name="PositionBuySellTab"
  cal:RegionManager.RegionName="{x:Static inf:RegionNames.MainRegion}"/>

    Silverlight4不支持X:Static。所以,你须要为区域名称使用字符串值,或者,可选的,为每一个区域名称定义一个应用程序级别的字符串资源。RegionName付附加属性能够绑定到这个字符串资源来解析区域名称。

XAML (Silverlight)
<Controls:AnimatedTabControl 
 Regions:RegionManager.RegionName="MainRegion" />

使用代码添加一个区域

    RegionManager能够不使用XAML而直接注册区域。下面的代码示例展现例如如何从后置代码文件中将一个区域添加到一个控件中。首先是要获取一个区域管理的引用。而后,使用RegionManager静态方法SetRegionManager和SetRegionName,区域附加到界面的ActionContent控件而后区域被命名为AcontionRegion。

C#
IRegionManager regionManager = ServiceLocator.Current.GetInstance<IRegionManager>();
RegionManager.SetRegionManager(this.ActionContent, regionManager);
RegionManager.SetRegionName(this.ActionContent, "ActionRegion");

在区域加载时显示区域内的视图

    在视图发现机制中,模块能够将视图(视图模型或者展示模型)注册到指定名称的位置。当这个位置在运行时被展示的时候,任何注册到这个区域的视图将会被自动地建立和展示。

    模块在一个注册表中注册视图。父视图查询这个注册表来发现这些注册到指定位置的视图。在它们被发现以后,父视图将会使用这些视图来替换掉屏幕上的控件站位符。

    在应用程序加载之后,组合视图被通知来处理已经被添加到注册表中的心视图的位置。

    下面的插图展现了视图发现方式。

clipboard[8]

    Prism类库定义了一个标准的注册表,RegionViewRegistry,来为这些命名的位置注册视图。

    为了在一个区域中显示一个视图,使用区域管理来注册视图,就像下面的代码展现。你能够在区域中直接注册一个视图类型,在这种状况下,经过依赖注入容器这个视图将会被构建而且在承载控件的区域加载的时候将视图添加到这个区域中。

C#
// View discovery
this.regionManager.RegisterViewWithRegion("MainRegion", typeof(EmployeeView));

    可选的,你能够提供一个返回显示视图的委托,例如所示的例子。区域管理器将会在区域建立的时候展现视图。

C#
// View discovery
this.regionManager.RegisterViewWithRegion("MainRegion", () => this.container.Resolve<EmployeeView>());

UI Composition QuickStart 在员工模块的ModuleInit.cs文件中有一个展现了如何使用RegisterViewWithRegion方法

的演示。

编程的方式显示区域内的视图

    在视图注入的方式中,视图管理它们的模块经过编程的方式添加到一个命名的位置或从这个位置移除。为了使用这点,应用程序在UI中包含了一个命名的位置的注册表。一个模块可使用这个注册表查找某个位置而后经过编程将视图注入到其中。为了保证注册表中的位置能够被简便的访问,每一个命名的位置附着于用于注入的视图的通用接口。下面的插图展现了视图注入方式。

clipboard[9]

    Prism类库为访问这些位置定义了一个标准的注册表,RegionManager,和一个标准的接口,IRegion

    为了使用视图注入来将一个视图添加到区域中,从区域管理中得到区域,而后调用Add方法,以下代码所示。在视图注入中,视图只有在模块加载时或者预约义动做完成时被添加到了区域中以后才能被展现。

C#
// View injection
IRegion region = regionManager.Regions["MainRegion"];

var ordersView = container.Resolve<OrdersView>();
region.Add(ordersView, "OrdersView");
region.Activate(ordersView);

    另外,Stock Trader RI, the UI Composition QuickStart中都有书图注入的演示。

导航

    Prism 4.0类库包含了为WPF和Silverlight应用程序中实现导航功能提供了丰富和兼容的导航API。

    区域导航是视图注入的一种形式。当一个导航请求被处理时,它将会尝试查找可以知足该请求的区域的视图。若是它找不到合适的视图,它将会调用应用程序容器来建立这个对象,而后将这个对象注入到区域中而后激活它。

    下面来自Stock Trader RI 中的 ArticleViewModel 代码示例展现了如何初始化一个导航请求。

C#
this.regionManager.RequestNavigate(RegionNames.SecondaryRegion, 
 new Uri("/NewsReaderView", UriKind.Relative));

    关于区域导航的更多信息,参考第8章“导航”。View-Switching Navigation QuickStart 和 State-Based Navigation QuickStart 也演示了程序导航的实现。

区域中视图排序

    不管使用视图发现仍是使用视图注入,应用程序均可能须要对在TabControlItemsControl,或任何其余的显示多个活动视图的控件中的视图进行排序。默认状况下,视图按它们被注册和添加到区域中的顺序进行显示。

    当一个组合应用程序构建时,视图常常从不一样的模块中进行注册。声明模块间的依赖关系能够缓解这个问题,可是当模块和视图都没有让你和实际的相互依存关系时,声明一我的工的模块间依赖耦合是没必要要的。

    为了容许视图能够参与它们自身的排序,Prism类库提供了ViewSortHintAttribute属性,这个属性包含了一个容许视图声明一个它应该如何在区域中排序的Hint的字符串属性。

    当展示视图时,Region类使用一个使用了默认的按提示来排序视图的视图排序惯例。这是一个简单的区分大小写的排序顺序。用于hint属性的视图被排到那些没有hint属性的视图的前面。一样,这些没有hint属性的视图按它们被添加到区域的顺序进行显示。

    若是你想要改变视图的排列顺序机制,Region类提供了一个你能够设置本身的Comparision<object>委托方法的SotrComparision属性。他对于代表区域反映在UI上Views和ActiveViews的排列顺序的属性是很是重要的。由于这些适配器例如ItemsControlRegionAdapter直接绑定到这些属性。一个自定义的区域适配器能够实现它本身的重载了区域如何排列视图的排序和筛选方式。

View Switching QuickStart演示了在导航区域的左手边的一个简单的数字模式来定义视图的顺序。下面的代码实例展现了ViewSortHint应用了每一个导航视图。

C#
[Export][ViewSortHint("01")]publicpartialclassEmailNavigationItemView
[Export][ViewSortHint("02")]publicpartialclassCalendarNavigationItemView

[Export][ViewSortHint("03")]publicpartialclassContactsDetailNavigationItemView

[Export][ViewSortHint("04")]publicpartialclassContactsAvatarNavigationItemView

多个区域之间共享数据

    Prism类库提供了视图之间通讯的多种实现方式。取决于你的应用场景。区域管理提供了RegionContext属性做为这些方式之一。

RegionContext在你想要在同一个承载的区域内的父视图和子视图之间共享上下文的时很是有用。RegionContext是一个附加属性。在区域控件的上下文属性设置值,那样可使得全部在区域控件中展现的子视图之间得到数据可用。区域上下文能够是任意的很是简单的或者负责的对象而且能够是一个数据绑定的值。RegionContext能够用于不管是视图发现中仍是视图注入中。

    注意:

    在Silverlight和WPF中,DataContext属性被用来设置视图的本地数据上下文。它容许视图使用数据绑定来同视图模型,本地表现,或者模型之间进行通讯。RegionContext用来在多个视图之间共享上下文,而且不支持定位到单独的视图。它提供了一种在多个视图之间共享上下文的简单机制。

    下面的代码展现了如何在XAML中使用RegionContext附加属性。

XAML
<TabControl AutomationProperties.AutomationId="DetailsTabControl"
  cal:RegionManager.RegionName="{x:Static local:RegionNames.TabRegion}"
  cal:RegionManager.RegionContext="{Binding Path=SelectedEmployee.EmployeeId}"
  ...>

    你也能够在代码中设置RegionContext,以下所示。

C#
RegionManager.Regions["Region1"].Context = employeeId;

为了在视图中取回RegionContext,使用RegionContext类的GetObservableContext静态方法。它将视图做为参数传递而且而后访问它的Value属性,以下所示。

C#
private void GetRegionContext()
{
  this.Model.EmployeeId = (int)RegionContext.GetObservableContext(this).Value;
}

    RegionContext的值能够从视图中经过简单的分配一个新值的Value属性改变。视图能够选择经过订阅GetObservableContext方法返回的ObservableObject对象的PropertyChanged事件来在RegiongContext属性发生改变时背通知。这使得多个视图在它们的RegionContext发现变化时保持同步。下面的代码演示了订阅PropertyChanged事件。

C#
ObservableObject<object> viewRegionContext = 
               RegionContext.GetObservableContext(this);
viewRegionContext.PropertyChanged += this.ViewRegionContext_OnPropertyChangedEvent;

private void ViewRegionContext_OnPropertyChangedEvent(object sender, 
                   PropertyChangedEventArgs args)
{
  if (args.PropertyName == "Value")
  {
    var context = (ObservableObject<object>) sender;
    int newValue = (int)context.Value;  
  }
}

    注意:

RegionContext被设置为区域的承载的内容对象的附加属性。这意味着内容对象必须派生自DependencyObject。视图是一个但是控件,它最终派生自DependencyObject。若是你选择使用WPF或者Silverlight数据模板来定义你的视图,内容对象将会表现为视图模型或者展示逻辑。若是你的视图模型或表现模型须要取回RegionContext,它须要派生自DependencyObject基类。

建立区域的多个实例

    范围内的区域是仅适用于视图注入。若是你须要一个拥有它本身的区域实例的视图的时候你应该使用它们。定义区域的视图的附加属性自动继承它们父区域的RegionMangaer。一般,这是一个在Shell窗口中注册的全局的RegionManager。若是应用程序建立了视图的多个实例,每一个实例将会尝试注册它的区域中的RegionManager。RegionMangaer容许名称不重复的区域;所以,第二次注入将会产生一个错误。

    做为代替,使用区域范围能够是每一个视图拥有它本身的RegionManager而且它的区域将会注册到RegionManager中而不是注册到父RegionMangaer,以下图所示。

clipboard[10]

    为了建立视图的一个本地RegionManager。指定的新的RegionManager应该在你添加视图到区域中时建立。以下所示。

C#
IRegion detailsRegion = this.regionManager.Regions["DetailsRegion"];
View view = new View();
bool createRegionManagerScope = true;
IRegionManager detailsRegionManager = detailsRegion.Add(view, null, 
                            createRegionManagerScope);

Add方法将返回新RegionManager该视图能够保留进一步访问本地范围。

建立Views

    应用程序的视觉展示能够采用多种形式,包括用户控件,自定义控件,和数据模板,仅举几例。在Stock Trader RI中,用户控件经常使用于在主窗口中展示不一样的部分,可是这不是一个标准。在你的应用程序中。你应该你应该使用你最熟悉和最适合你工做的方式。不管您的应用程序为主视觉表现,你将不可避免地使用用户控件,自定义控件和数据模板的组合在总体设计中。下面的插图展现了Stock Trader RI中中使用的几项。这个插图也做为后面几节的服务,在后面将会单独描述每项。

clipboard[11]

用户控件

    Blend和Visual Studio 2010都为建立用户控件提供了丰富的支持。使用这些工具建立的用户控件所以被推荐使用Prism类库建立UI内容。如本主题前面提到的,Stock Trader RI普遍的使用它们建立了将会插入到区域中的内容。WatchListView.xaml用户控件就是内部包含了WatchModule的一个简单的UI展示的很好的例子。这种控件是简单的使用这种模型的简单控件。

自定义控件

    在一些状况下,一个用户控件太受限了,在这些状况下,自定义布局或者可扩展性比这种易建立性更重要了。这就是自定义控件很是有用的地方了。在the Stock Trader RI中,饼状图控件就是一个很好的例子。这个控件是从所述位置导出的数据组成的。而且显示为一个图标的总体组合。建立这种类型的控件比建立用户控件更具一些小的挑战性。相比于用户控件,它限制了Blend和Visual Studio2010对可视化设计的支持。

数据模板

    数据模板是这种数据驱动应用程序的一个重要的部分。在the Stock Trader RI中,使用基于集合的控件对数据模板的使用很是流行。在许多状况下,你可使用一个数据模板来建立一个完整的可视化展示而不须要建立任何类型的控件。ResearchRegion使用了一个数据模板来显示文章并与一个物品风格相结合,提供了一种指示其项被选定。

    Blend对于数据模板有丰富的可视化设计支持。Visual Sutdio 2010只提供了使用XAML编辑器来编辑数据模板的支持。

资源

资源例如样式,资源字典和控件模板能够分散在应用程序中。在一个组合的应用程序中尤为是这样。当你考虑在哪里放置资源,特别要注意UI元素和它们所须要的资源之间的依赖关系。在Stock Trader RI 解决方案中,以下面的插图所示,包含了代表各类资源能够存在的地方的标签。

clipboard[12]

应用程序资源

    一般,应用程序资源是能够被整个程序访问的资源。这些资源每每集中在应用程序根上,可是它们也能够提供一个为模块和控件的基本类型的默认样式。这样的一个例子是应用到根应用文本框中键入文本框样式。这种风格将提供给全部文本框的应用程序,除非被模块或控件水平的样式覆盖。

模块资源

    模块资源扮演着与应用程序根资源相同的角色,它们而已被模块中全部的项目使用。使用这种级别的资源能够为整个模块提供统一的界面而且也能够容许重用在跨越一个或多个可视化组件的指定实例。模块级别的资源的使用应该被包含在各自的模块中。建立模块之间的依赖关系可能会致使难以在用户界面元素显示在错误的定位问题。

控件资源

    控件资源一般包含在空间类库中,而且能够用于整个控件库中全部的控件。这些资源趋于用于最有限的区域内,由于控件库一般包含特定的控件而且不包含用户控件(在基于Prism类库的应用程序中,用户控件一般放在它们被使用的模块中)。

UI设计指导

    本主题的目的是为XAML设计人员和使用Prism类库开发WPF或Silverlight应用程序的开发人员体统一些高级别的指导。本主题描述了UI布局,可视化展示,数据绑定,资源和展示模型。在阅读了本主题以后,你将会对于如何基于Prism类库设计应用程序的UI有更深层次的理解。以及理解一些能够帮助你建立易维护UI的组合应用程序的技术。

用户界面设计指导

    基于WPF和Silverlight标准规则的使用Prism类库建立的组合应用程序的布局---使用了包含相关项目的面板概念的布局。然而,在组合应用程序中,面板内的各类内容是动态的而且在设计时是并不被知道的,这就要求设计人员和开发人员建立能够包含布局内容的页面框架而后设计适应这些布局的元素。做为一个设计人员或者开发人员,这意味这你必须考虑Prism类库中的两个主要的布局概念:容器组合和区域。

容器组合

    容器组合在实际上仅仅是WPF和Siliverlight本质上提供的容量模型的一种扩展。container次能够表明任何元素,包含窗口,页面,用户控件,面板,自定义控件,控件模板,或者数据模板,它能够包含其余元素。

    你如何想象你的UI可能因实现而不一样,可是你将会发现反复出现的主题。你将会建立一个窗口,页面或者包含了固定内容和动态内容的用户控件。固定的内容将会包括包含的UI内容的整个架构,动态内容将会是区域内的一个位置。

    例如,WPF Stock Trader RI有一个名为Shell.xaml的窗口包含了应用程序的总体结构。下面的插图展现了在Blend中加载的Shell。注意只有一些固定的UI部分是可见的。Shell的其余部分是在应用程序加载模块时动态的插入到不一样的区域内的。

    在这样类型的应用程序中设计时的经验会有一些限制,可是在实际上你所知道的将会在运行时被放到不一样的区域内的内容正是你须要设计的。来看这样的一个例子,比较如下下一张插图的主页面的设计和后面的一张插图中运行时的视图。在设计人员的视图中,与运行时的包含了一个包含一个位置数据的Tab控件,一个趋势线,饼图和与选中的股票相关的新闻区域的界面相反,这个页面绝大部分是空的。在设计人员的视图与运行时的视图的不一样展现了设计人员和开发人员在它们使用Prism类库建立应用程序时面临的调整。

    设计时看不到这些项,所以肯定它们多大以及它们怎样适应整个应用程序的界面就有一些困难。在你建立你的容器的布局时考虑如下几点:

  • 是否有任何尺寸的限制,是否可能限制内容的大小?若是有的话,考虑使容器支持滚动。
  • 考虑使用Expand而和ScrollViewer来整合须要将大量的动态的适应到有限的空间中的状况。
  • 密切关注在屏幕内容增加时内容将会如何放大来确保你的应用程序界面能够适应任何分辨率。

clipboard[13]

clipboard[14]

在设计时整合应用程序视图

    前面两个插图中,在工做的挑战之一就是在运行时组合高级别的视图。在组合应用程序中的每个UI元素都必须被单独的设计。这使得很难想像在运行时的组合页码将会是什么样子。为了可以想象到组合视图的在组合状态下的样子。你能够建立一个包含了你想要测试的全部UI元素的视图的测试工程。

    另外,考虑使用Blend工具中的设计时的样例数据功能和Visual Studio 2010来使用数据填充UI元素。设计时数据对于使用数据模板,集合控件,图,表等工做很是有帮助。更多信息,请看“设计时样例数据指导”一节。

布局

    当你设计一个组合应用程序的布局时考虑如下几项:

  • Shell定义应用程序的主要布局。每一个布局的面是一个区域而且应该保持为一个空的容器。不要在设计时替换区域内的内容由于内容将会在运行时加载。
  • Shell应该包含背景,标题和页脚。将Shell想象成ASP.NET中的母版页。
  • 若是shell区域有固定尺寸,视图应该使用动态大小。
  • 视图可能会要求固定高度和动态宽度。这一点的一个例子就是Stock Trader RI中侧边栏的PositionPieChart视图。
  • 其余视图可能要求动态高度和宽度。例如。Stock Trader RI中一侧边栏的NewsReader。它自身的高度取决于标题的长度,而且它的宽度应该一直适应区域的大小(侧边栏的宽度)。相同的应用项就是PositionSummaryView视图中的表格的宽度应该适应屏幕的大小以及高度应该适应表格中行数量。
  • 视图一般应该有透明的背景,使得Shell背景来做为应用程序的可视化的背景。
  • 常常为指定颜色,画刷,字体和字体大小使用命名的资源,而不是直接在XAML中指定属性值。这样可使应用程序更便于维护。这也使得应用程序可在运行时反映资源字典的变化。

动画

    当你在在Shell或者视图中使用动画的时候考虑如下几点:

  • 你能够在Shell的布局上使用动画,可是你必须对它的内容和视图单独的应用动画。
  • 每一个视图单独的设计和设置动画
  • 使用平滑柔和的动画来提供一个UI元素进入视图或从视图中移除的可视化线索,这给应用程序一种光滑的外表和感受。

    Blend提供了一系列丰富的行为,缓动函数,和一个优秀的动画编辑体验以及基于可视化状态变化或事件的转换UI元素。更多信息,请看MSDN上的"VisualStateManager Class"

运行时优化

    为性能优化考虑如下几项:

  • 将任何的通用资源放置在App.xaml文件或使用合并资源字典来避免样式的重复
  • 在Silverlight中,避免为指定的应该与应用程序其余部分(例如,应用程序主题)区别样式的静态的文本使用非系统的字体。在这种状况下,肯定是否将文本转换为路径或者使用嵌入的字体会更好。嵌入一种字体可能影响到下载.xap文件的大小由于有些字体文件至关大。为了最小化下载.xap文件的大小,Blend容许你之下在字体的符号特征来替代整个字体。更多信息请看"Using Custom Fonts in Silverlight."

设计时优化

     接下来的几节将会描述设计时的场景而且为最大限度地利用了设计时体验提供解决方案。

拥有许多XAML资源的大的解决方案

    在应用程序中有许多XAML资源是解决方案的一部分,可视化设计器加载时间将会收到影响,某些状况下将会很是严重。由于可视化设计器必须转化合并XAML资源,这会存在性能的降低。解决这个问题的方案就是将全部的XAML资源移到另外一个解决方案中,编译那个解决方案,而后从那个大的方案的DLL中引用新的XAML资源。由于XAML资源在引用的二进制程序集中,可视化设计器将会不转换XAML资源,这样就提升了设计时的性能。当移动XAML资源到一个扩展的程序集时,你可能须要考虑为你的资源暴露CompoentResourceKeys。更多信息,请看MSDN上的“"ComponentResourceKey Markup Extension”。

XAML 资产

    XAML对于建立例如图片,图形和3D场景等资产来讲是一种有力和富有表达性的语言。一些开发者和设计者偏向建立XAML资产来替代使用.ico,.jpg,或者.png图片文件。一个缘由是选择XAML的方式利用了依赖于XAML渲染解决方案的优点。另一个缘由是它们可使用Expression工具包中的工具来创须要的资产来设计它们的应用程序。

    若是解决方案中有许多这样的资产,设计器在在设计时的加载效率就会受到影响,将这些资产转移到其余的单独的DLL中能够解决这个性能问题。将资产移动到单独的DLL中还能够在多个解决方案中达到重用这些资产的目的。

可视化设计与引用程序集

    一个移动XAML资源和资产到一个引用的二进制程序集中的不幸的负面影响就是Blend和Visual Studio 2010 的编辑器可能不会列出位于二进制引用的程序集资源。这意味着你将不可以从工具提供的资源选择器中挑选一个命名的资源了。相应的,你将须要资源名称的类型。

Silverlight 设计时App.xaml 资源

    Silverlight的组合应用,能够构成多种方式,以容许组件的运行时的延迟加载,或减小初始的.xap下载大小。你可使用一个策略是建立主Silverlight应用程序,而后建立每一个附属程序集的模块。当你添加附属程序集时,你能够选择其中一个应用程序项目模板或者Silverlight类库模板。

    为附属程序集选择一个Silverlight应用程序项目模板能够提供一个部署的好处:当程序集建立时将会被打包成一个.xap文件。然而,这样的作法对于可视化设计器的负面影响就是当不知一个Silverlight应用程序表如今单独的解决方案中时。主Silverlight应用程序须要将它的应用程序资源在App.xaml中合并。这样的问题就是可视化设计器会消耗暴露在App.xaml中的资源。由于在解决方案中有不止一个Silverlight应用程序。可视化设计器不会尝试从另外的Silverlight应用程序中使用资源;想法,它只会使用正在活跃着的Silverlight应用程序的那些资源。

    Blend 4提供了这个问题的一个解决方法。当Blend察觉到这种状况是,它会弹出一个让你选择一个用于解决方案中全部工程之间的设计时资源字典的对话框。

    Visual Studio 2010 没有这样的功能;所以,附属的Silverlight应用程序集将不会有很是丰富的设计时体验触发你在本地程序集中合并了应用程序级的资源。若是你为了一个更好的可视化设计体验而选用合并应用程序级资源。你必须记住在你部署你的应用程序以前将它们移除。

建立设计友好的Views指导

    如下是设计一个友好的应用程序拥有的一些特征:

  • 同使用Visual Studio 和Blend设计器它提供了丰富的编辑体验
  • 它是工具可用的(Tooling-enabled).例如,它容许你使用绑定构建工具
  • 它在须要时提供了一个设计时的样例数据
  • 它容许在设计时执行代码而不引发未处理的异常

如下动做在编辑期间将会执行屡次。设计者不友好的用户代码将会引发一个或多个这些动做失败,这将会削减开发者好设计者的生产率和创造率。

  • 设计界面动做:
    • 构建对象
    • 加载对象
    • 设置属性值
    • 执行设计界面的手势
    • 使用一个控件做为跟元素
    • 在一个控件中承载另外一个控件
    • 重复性的打开关闭XAML文件
    • 从新构建项目
    • 从新加载设计器
  • 绑定构建动做:
    • 发现DataContext
    • 列出可用的数据源
    • 列出数据源类型属性
  • 设计时样例数据动做:
    • 在设计界面使用控件正确的展现样例数据

设计时编码

    为了给你一个丰富的设计时的体验,Visual Studio和Blend设计器在设计时实例化了对象而且运行代码。然而,因为在类型被实例化以前尝试访问其引用而引发的空引用异常而致使加载失败的高百分率以及没必要要的设计时异常。

    下表列出了差的设计时体验的主要缘由。经过避免如下问题及使用技术减轻这些问题,设计时体验和生产率将会极大的加强,而且开发人员与设计人员之间的工做流程也将会更加顺畅。

避免这些用户代码

Visual Studio 2010

Blend 4

在设计时旋转(Spinning)多个线程,例如,在设计时的Loaded事件或者构造方法中中实例化和开始一个Timer。

X

X

在设计时使用引发栈溢出的控件。

使用尝试递归加载自身的控件

X

X

在转换器或者数据模板选择器中抛出空引用异常

X

X

在构造方法中抛出空引用异常或其余异常。 它们一般有如下引发:

  • 在设计时使用调用从数据库或者网络返回数据的业务或者数据层的代码
  • 尝试经过MEF,控制反转,或者位置定位器在启动引导程序或容器初始化代码运行以前解析依赖关系

X

X

在控件或者用户控件的Loaded事件中抛出空引用异常或者其余异常。这种常在你假设控件的状态在运行时多是true可是在设计时却不是true时发生

X

X

在设计时尝试获取Application或Application.Current对象

X

X

在WPFUserControls中使用StaticResource.

X

建立很是大的项目

X

设计时 用户代码问题缓解

    少许的预防性代码实践将会消除前面表格中描述的大部分的问题。然而,在你能够缓解设计时用户代码问题以前,你必须理解你的应用程序控件和代码将会被设计器在的一个未初始化的应用程序域中隔离的执行。在这里,未初始化的意思是指一般的启动,引导程序,或者初始化代码还未运行时。

    当你的应用程序在运行时执行的时候,App.xaml.cs文件中的启动代码将会运行。若是你的程序中有依赖哪里的代码,这些代码将会不在设计时执行。若是你尚未预料到你这里的代码,将会发生没必要要的异常。(这就是为何在设计时尝试获取Application或Application.Current对象将会引起异常的缘由了),为了缓解这些问题:

  •     用于不要假设引用的对象将会在设计时代码中被实例化。在设计时能够被执行的代码,一直执行在获取任何引用对象前检查是否为空。
  • 若是你的代码访问了Application或者Application.Current对象,在获取这个对象以前进行是否为空的检查。
  • 若是你的构造方法或者Loaded事件处理程序中须要运行复杂的代码或者须要访问数据库或调用网络结果的代码,考虑如下几种解决办法:
    • 将代码包装到一个经过调用如下之一的System.ComponentModel DesignerProperies方法来肯定代码是否在设计是运行的检查中
      • WPF:DesignerProperties.GetIsInDesignMode
      • Silverlight:DesignerProperties.IsInDesignTool
    • 不是在构造器或者Loaded事件处理程序中直接运行代码,而是将这些调用抽象到接口后面的一个类中,而后使用许多技术之一来解析不一样的依赖信息在设计时,运行时和测试时。例如,不是直接调用数据服务来检索数据,而是将数据服务调用包装到一个经过接口暴露方法的类中,而后,在设计是,使用模拟或这设计时对象来解析这个接口。

设计时理解用户控件代码什么时候

    Blend和Visual Studio 都使用根对象的模型在设计器面板中展现。这对于提供被要求的设计体验就很是必要了。由于跟对象是被模拟的,它的构造器和Loaded事件代码在设计时不会被执行。然而,场景中的剩余的控件被正常的构造,而且它们的Loaded事件会像运行时同样引起执行。

    在下面的插图中,根Windows构造器和Loaded事件代码不会被执行。子用户控件的构造器和Loaded事件将会被执行。

clipboard[31]

    这些概念很是重要,尤为是若是你正在构建组合应用程序或者构建那些在运行时动态构建的应用程序。

    大多数的视图是单独编码和设计的。由于它们被单独的设计,一般在设计器中它们就是根对象。所以,它们的构造器和Loaded事件代码永远不会执行。

    然而,若是你将相同的用户控件放到设计界面的另外的一个控件中,这个被隔离的用户控件的代码在设计时就会马上执行。若是你没有遵循上述剪切设计时代码问题的实践,承载控件将会变得不友好而且引发设计器的加载问题。

设计时属性

    内建的“d:”设计时属性提供了一个成功达到设计时工具体验的平滑的道路。

    咱们须要解决的问题是如何提供一个Shape来在设计时绑定这个构造工具。在这种状况下,Shape就是一个实例化的能够在绑定的构建器上反映的类型,而后在构建绑定时列出这些属性。

Shape也提供了设计时样例数据,样例数据在“设计时样例数据指导”一节讲到。

    “d:”属性和扩展标记是设计命名空间的一个别名,这个设计了一系列的属性。下面是在MSDN上一些涉及到“d:”属性和扩展标记的链接:

“d:”属性和扩展标记不能在代码中被建立和扩展;它们只能在XAML中使用。“d:”属性和扩展标记不被编译到你的应用程序中;它们只是在Visual  Studio 和Blend工具中使用。

d:DataContext属性

    d:DataContext,为控件和子控件指定设计时数据上下文。当指定d:DataContext时,你也应该一同提供与运行是DataContext同样的Shape的设计时DataContexgt。

若是为一个控件同时指定了DataContextd:DataContext,开发工具将会使用d:DataContext。

d:DesignInstance 标记扩展

    若是扩展标记对于你来讲仍是新知识,请阅读MSDN上的"Markup Extensions and WPF XAML"。

    d:DesignInstace返回一个你将会在设计器中想要将其指定为控件绑定的数据源的类型(“Shape”)实例。这个类型不须要去建立来用于肯定Shape。下表解释了 d:DesignInstace扩展标记的属性。

扩展标记属性

定义

Type

将被建立的类型的名称。类型是构造方法中默认的参数。

IsDesignTimeCreatable

指定的类型是否可用被建立?若是不能够, 一个仿类型而不是真实的类型将会被建立。默认值是false

CreateList

若是是真,返回一个制定类型的通常集合。默认值是false

d:DataContext经常使用场景

    下面的三个示例代码演示了一个链接最多的视图和视图模型的可重复的模式,以及可用的设计工具。

PersonViewModel PersonView运行是的一个依赖项。然而,在实例的视图模型是很是的简单的。真实世界里的视图模型一般会拥有一个或多个额外须要解析的依赖,而且这些依赖项一般被注入到它们的构造器中。

    当PersonView被构建时,它依赖的PersonViewModel将会被构建而且它的依赖关系将会经过一个依赖注入容器被解析。

    注意:

    若是视图模型没有须要解析的额外的依赖项,视图模型能够在视图的XAML中北实例化,而且不会要求它的DataContext和d:DataContext。

C# PersonViewModel.cs
[Export]
public class PersonViewModel {

 public String FirstName { get; set; }
 public String LasName { get; set; }

}
C# PersonView.xaml.cs
[Export]
public partial class PersonView : UserControl
{
 public PersonView()
 {
  InitializeComponent();
 }

 [Import]
 public PersonViewModel ViewModel
 {
  get { return this.DataContext as PersonViewModel; }
  set { this.DataContext = value; }
 }
}

    这是链接视图和视图模型的很好的模式;然而,它使得在设计时视图并不知晓它的DataContext的shape(视图模型)。

    在下面的XAML实例中,你能够看到d:DesignInstance扩展表演用于Grid来返回一个PersonViewModel的仿的实例。这个实例经过d:DataContext被暴露。做为结果,Grid的子空间将会继承d:DataContext,使得设计工具能够发现并使用它的类型和属性,结果就是使得开发者和设计者的更生产率的设计体验。

XAML PersonView.xaml
<UserControl 
 xmlns:local="clr-namespace:WpfApplication1"
 x:Class="WpfApplication1.PersonView"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
 mc:Ignorable="d" 
 d:DesignHeight="300" d:DesignWidth="300">

 <Border BorderBrush="LightGray" BorderThickness="1" CornerRadius="10" Padding="10">

  <Grid d:DataContext="{d:DesignInstance local:PersonViewModel}">
   <Grid.RowDefinitions>
    <RowDefinition Height="Auto" />
    <RowDefinition Height="Auto" />
   </Grid.RowDefinitions>
   <Grid.ColumnDefinitions>
    <ColumnDefinition Width="100" />
    <ColumnDefinition Width="Auto" />
   </Grid.ColumnDefinitions>

   <Label Grid.Column="0" Grid.Row="0" Content="First Name" />
   <Label Grid.Column="0" Grid.Row="1" Content="Las Name" />

   <TextBox 
    Grid.Column="1" Grid.Row="0" Width="150" MaxLength="50" 
    HorizontalAlignment="Left" VerticalAlignment="Top"
    Text="{Binding Path=FirstName, Mode=TwoWay}" />
   <TextBox 
    Grid.Column="1" Grid.Row="1" Width="150" MaxLength="50" 
    HorizontalAlignment="Left" VerticalAlignment="Top"
    Text="{Binding Path=LasName, Mode=TwoWay}" />

  </Grid>
 </Border>

</UserControl>

    注意:

    附加属性和视图模型定位器解决方案

    在开发者社区里还有几种可选的用于关联视图和视图模型的技术。其中一种解决方案在运行时起到很强的做用可是在设计时一般不起做用。这种解决方法之一就是使用附加属性和视图模型定位器来关联到视图的DataContext。视图模型定位器被要求使用是由于那样视图模型能够被构建以及它的依赖项能够被解析。

    这种解决方案的问题是你必须也要包含d:DataContext-d:DesignInstance组合由于可视化设计器工具不能反映d:DesignInstance的附加属性的结果。不管你在应用程序中实现的哪一种技术来解决设计时shape,最重要的目标就是在应用程序中保持一直。一致性可使得应用程序维护起来更加见到那而且将产生成功的工做流程。

设计时样例数据指导

    WPF和Silverlight 设计团队发布了深刻的,基于场景针对训练的文章讨论了在WPF和Silverlight项目中使用样例数据。这篇文章

"Sample Data in the WPF and Silverlight Designer,"能够在MSDN上获取。

使用设计时样例数据

    若是你使用一个可视化设计工具,好比Blend或者Visual Studio 2010,设计时样例数据变得很是重要。视图能够被数据和图片填充,这使得设计任务变得更容易更快度的完成。这样的结果就是生成率和创造力的提升。

    包含数据模板的空集合控件只有在被填充了数据以后才会可见。这使得编辑空集合控件的任务话费更多的时间由于你须要运行应用程序来查看最近一次修改后在运行时的样子。

样例数据源

    你可使用如下任何一种来源的样例数据:

  • Expression Blend XML样例数据
  • Expression Blend 4和Visual Studio 2010 XAML 样例数据
  • XAML 资源
  • 代码

    这些来源的每一种数据将会在下面讲述。

Expression Blend XML 样例数据

    Expression Blend中为您提供了快速建立一个XML模式和填充相应的XML文件的能力。这种实现不依赖任何项目类。

    样例数据类型的目的是让设计人员可以快速的开始它们的项目,不须要等待一个开发人员或者在应用类能够被使用以前。

    虽然大多数的样例数据都支持Blend和Visual Studio 设计器,可是XML样例数据只是Blend的功能且并不支持在Visual Studio 设计器中呈现。

    注意:

    在项目构建时XML样例数据不会被编译或者添加到程序中;然而,XML S车马会被编译到构建的程序集中。

Expression Blend 4 and Visual Studio 2010 XAML 样例数据

在Blend 4和Visual Studio 2010的开始,d:DesignData扩展标记被添加尽可能,这使得设计时能够加载XAML样例数据。

    样例数据XAML文件包含了一个或多个类型及关联到其属性的值的实例的XAML。

    d:DesignData有一个使用一个统一资源定位符(URI)来获的位于项目中XAML文件中的样例数据的Source属性。d:DesignData扩展标记加载    XAML文件,转换而后返回一个对象图表。这个对象图标能够经过d:DataContext属性,CollectionViewSource d:DesignSource 属性, 或 DomainDataSource d:DesignData 属性被使用。

    d:DesignData扩展标记客服的挑战之一就是它得意建立非可建立的样例数据。例如,WCF Rich Internet Application (RIA)服务实体---派生的对象不能在代码中建立。另外,开发人员可能会有他们自已的不能建立的类型,可是仍然但愿拥有这些类型的样例数据。

    你能够在Solution Explorer 中经过设置样例数据文件Build Action属性来改变d:DesignData来如何处理你的样例数据文件,以下:

  • Build Action = DesignData – 仿制类型将会被建立
  • Build Action = DesignDataWithDesignTimeCreatableTypes – 真实类型将会被建立

    注意:

    在下面的插图中,Custom Toll属性是空的。这就要求样例数据可以恰当的工做。默认状况下,Blend  经常会设置这个属性为空值。

    当你使用Visual Studio 2010来添加一个样例数据文件时,你一般会添加一个资源字典项而且从哪里编辑它。在那种状况下,你必须设置Build Action而且清空Custom Tool属性。

clipboard[32]

    Blend提供了快速建立和绑定XAML样例数据的工具。XAML样例数据能够在Visual Studio 2010 设计器中被使用和查看,以下图所示。

clipboard[33]

    在生产了样例数据以后,数据将会显示在数据面板中,以下图所示。

clipboard[34]

    而后你能够将它拖到视图中的根元素上,例如UserControl,而且将它设置到d:DataContext属性。你也能够将样例数据拖拽到items Controls上,而且Blend将会将数据与控件进行关联。

    注意:

    XAML样例数据不会被编译或者包含到构建的程序集中。
XAML资源

    你能够在XAML中建立一个包含了指望类型的实例的资源,而后将那个资源绑定到DataContext属性或者一个集合控件上。

    这种技术能够用于快速建立用于若是不使用样例数据将会花费很长时间来编辑数据模板的舍弃式样例数据。

代码

    若是你喜欢在代码中建立样例数据,你能够写一个暴露属性或方法来为它们的使用者返回数据的类。例如,你能够写一个在类中默认的空构造方法中填充了多个Customer类的实例的Customers类。每个Customer实例将会有合适的属性值来设置。

    你可使用样例数据类的一种方法是使用前面讲到的d:DataContext,d:DesignInstace组合,确保将

d:DesignInstanceIsDesignTimeCreatable属性设置为true。

IsDesignTimeCreatable必须设置为true的缘由是你想要使用者构造方法来执行那样类中的代码将会运行。若是使用者被做为一个模仿类型来对待,使用者的代码将永远不会运行而且只有“shape”将会被工具发现。

下面的XAML实例实例化了Customers类,而且将其设置到d:DataContext属性。Grid的子控件可使用Customers类暴露的数据。

XAML
<Grid d:DataContext="{d:DesignInstance local:Customers, IsDesignTimeCreatable=True}">

UI布局关键决定

    当你开始一个组合应用程序项目时,须要作一些未来会很难改变的UI设计决策。通常来讲,这些决策时普遍应用的而且他们的一致性将会帮助开发者与设计者的生产力。

    如下是一些重要的UI布局决策:

  • 决定应用程序流并相应地肯定区域
  • 决定每一个区域将会使用的加载的视图类型
  • 决定你是否想要使用区域导航API
  • 决定你将会使用那种设计模式(MVVM,展示模式等等)
  • 决定样例数据策略

更多信息

关于扩展Prism类库的更多信息,请参考"Extending Prism."

关于命令的更多信息,请参考第5章, "Implementing the MVVM Pattern."的"Commands"

关于绑定的更多信息,请参考 第5章,"Implementing the MVVM Pattern.""Data Binding"

关于区域导航的更多信息,请参考第8章, "Navigation."

关于本主题中讨论的指导内容,请参考如下:

相关文章
相关标签/搜索