标准的应用程序自然地支持菜单驱动导航,而浏览器应用程序自然地支持超链接导航。但是Windows Presentation Foundation应用程序模型能够让两者混合。
Windows Presentation Foundation资源
到目前为止,我们讨论了嵌入应用程序部件的页面。但是,内容可以从很多地方载入--它可能被嵌入了代码所使用的部件、可能被嵌入一个被引用的部件、或者由一组松散的内容文件组成,根本没有嵌入任何部件。松散的内容本身可以位于本地磁盘、文件共享、甚至于Web站点上。并且,无论内容是嵌入的还是松散的,它都不一定是页面;内容可能包括多种媒体,例如图像、视频和音频。最后,内容也不一定属于某个特定的应用程序。属于其它Web应用程序的HTML页面也是可行的。
这种灵活性允许开发者更简单地处理大量的现实问题。有时候内容对于应用程序来说足够特别,而且该应用程序是如此依赖这个内容,以至于需要把内容嵌入部件中来部署内容和应用程序。有些应用程序的内容经常变换,以至于重新构建部件和重新部署新内容变得不切实际,因而支持松散的内容(由于松散的内容可以位于通常的位置,基于Internet和内部网的XBAP应用程序可以避免下载不必要的部件)。此外,有些内容在多个应用程序之间共享,但是仍然需要保证它们可供使用。
为了保证灵活性,Windows Presentation Foundation为了唯一标识和载入资源,使用了一种特殊的机制。它不考虑内容的位置或内容是嵌入还是松散的。这种机制的基础是Pack URI大纲(scheme),它是一种用不同的URI标识应用程序资源的可扩展大纲。Windows Presentation Foundation利用Pack URI大纲来支持几种用于载入内容的不同的、但是常见的情形。
在整篇文章中,无论什么时候使用Application.StartupUri 和Hyperlink.NavigateUri,示例代码都使用Pack URI来标识和载入窗体和页面:
<!--App.xaml (markup)--> <Application ... StartupUri="HomePage.xaml" /> |
这个例子使用了Pack URI的相对版本,它是一种很好的简化操作,允许你输入更少的内容。Pack URI的完整版本如下所示:
pack://application:,,,/HomePage.xaml |
完整的Pack URI由三个关键的部分组成:大纲(pack://)、拥有者(application:)和路径(,/HomePage.xaml)。其中拥有者描述了拥有资源的容器的类型,而路径描述了资源与容器的相对位置。"application:"容器是一个真正的部件,而路径是资源相对部件的根(root)的位置。
不管使用完整的或相对的Pack URI,它所指向的内容即可以嵌入部件的内部,也可以是存储在与应用程序执行文件相关的某个位置的松散的XMAL文件。对于一个存放在应用程序可执行文件目录中的松散XAML页面来说,其Pack URI如下所示:
pack://application:,,,/HomePage.xaml |
有趣的是,这个松散的XAML文件的Pack URI与嵌入部件的Pack URI相同。为了区分两者,Windows Presentation Foundation使用了一个基本的解析机制,在查找松散资源之间,它首先在部件中查找嵌入的资源。
Pack URI还用于访问那些嵌入被引用的部件中的Windows Presentation Foundation资源,只是有细微的语法差别:
pack://application:,,,/BoxApplicationLibrary;component/HomePage.xaml |
相对的Pack URI等同于:
/BoxApplicationLibrary;component/HomePage.xaml |
Pack URI允许你从应用程序的原始站点(载入应用程序的位置)来定位和载入资源。对于Web服务器上载入的XBAP应用程序来说,让内容保持在当前位置同把新内容放入应用程序的发布位置一样简单。为了访问原始站点的松散资源,你必须使用另外一个特定类型的Pack URI,它只能使用完整路径:
pack://siteoforigin:,,,/HomePage.xaml |
你可以导航到页面(无论是嵌入的还是松散的)的任何一个片段(fragment)。这与Web样式的片段导航是类似的。通过指定Name属性,你就可以在页面上定义片段了,如下所示:
<Page ... > <TextBlock Name="Paragraph1" TextWrapping="Wrap"> ... </TextBlock> </Page> |
为了导航到页面片段,你需要使用另外一种特殊的Pack URI,在页面URI上附加"#XAMLElementName",如下所示:
HelpPage3.xaml#Paragraph3 |
页面函数(PageFunction)
由于内容来自于多个位置,而超链接驱动的应用程序又允许用户导航到任何位置,从而使完成一项事务变得非常困难。这是因为超链接驱动的应用程序不能轻易地约束用户导航到特定页面。无论应用程序提供了多少个超链接,用户仍然可以使用浏览器的地址栏导航到任何地方。其结果是,用户可以离开发起事务的页面,而不考虑事务是否完成了。在Web世界中,有很多技巧可用于建立类似对话框样式行为、依赖对话状态等的Web页面。不幸的是,这需要大量的开销。对话框的确可以解决这个问题,但是由于安全性原因,Window不能从应用程序中实例化,例如XBAP的对话框就只能在Internet区域部分信任环境中运行。
幸运的是,Windows Presentation Foundation通过页面函数支持模式对话框样式的机制。它们被封装为泛化的PageFunction类型,直接衍生自Page。因而PageFunction的样子与页面类似,它的建立方式也相似,如下段所示。
代码:PageFunction
<!--OrderABoxPageFunction.xaml (markup) --> <PageFunction xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:BoxApplicationXBAP" x:Class="BoxApplicationXBAP.OrderABoxPageFunction" x:TypeArguments="local:Order" WindowTitle="Box Application - Order a Box" > ... <!--Content--> ... <PageFunction>
// OrderABoxPageFunction.cs (code-behind) public partial class OrderABoxPageFunction: PageFunction<Order> { ... } |
这个特定的页面函数的目标是收集新订单的信息,它被Order封装了。由于典型情况下事务都是这样操作数据的,PageFunction是一个泛型(generic),并被声明用于操作特定的数据(标记中特定的x:TypeArguments属性)。如果x:TypeArguments的值和泛化的PageFunction类型的参数不匹配,就会出现编译错误。
调用PageFunction的页面需要实例化PageFunction并手动导航到该页面上:
// HomePage.cs (codebehind) public partial class HomePage : Page { void orderHyperlink_Click(object sender, RoutedEventArgs e) { OrderABoxPageFunction pageFunction = new OrderABoxPageFunction(); pageFunction.Return += new ReturnEventHandler<Order>(OrderABoxPageFunction_Returned); this.NavigationService.Navigate(pageFunction); } ... } |
接下来,PageFunction在给调用页面返回结果之前,必须允许用户完成页面:
// OrderABoxPageFunction.cs (codebehind) public partial class OrderABoxPageFunction: PageFunction<Order> { void orderHyperlink_Click(object sender, RoutedEventArgs e) { // 返回订单 this.OnReturn(new ReturnEventArgs<Order>(this.order)); } void cancelHyperlink_Click(object sender, RoutedEventArgs e) { // 取消订单 this.OnReturn(null); } ... } |
接下来调用PageFunction.OnReturn返回,传递一个泛化的ReturnEventArgs实例。如果事务被接受了,它就包含一个PageFunction所操作的类型实例。否则,它就是空的。为了检测PageFunction的返回,并获取ReturnEventArgs和其数据,调用页面需要处理PageFunction.Returned事件,如下段代码所示。被返回的数据存放在Returned事件处理程序的ReturnEventArgs参数的Result属性中。
代码:PageFunction.Returned
// HomePage.cs (code-behind) public partial class HomePage : Page { // 载入页面函数 void orderHyperlink_Click(object sender, RoutedEventArgs e) { OrderABoxPageFunction pageFunction = new OrderABoxPageFunction(); pageFunction.Return += new ReturnEventHandler<Order>(OrderABoxPageFunction_Returned); this.NavigationService.Navigate(pageFunction); } // 处理页面函数的返回 void OrderABoxPageFunction_Returned(object sender, ReturnEventArgs<Order> e) { if (e != null) this.orders.Add(e.Result); } ... } |
你可能希望在事务完成之后,确保PageFunction从导航历史中移除。这只需要一个简单的配置:
<!--OrderABoxPageFunction.xaml (markup) --> <PageFunction RemoveFromJournal="True" ... > ... <!--Content--> ... <PageFunction> |
通过从导航历史中删除页面函数,你可以阻止用户导航回该页面函数。这是非常重要的,因为如果不这样处理,用户就可能修改那些已经发生改变的数据,从而造成潜在的数据不一致风险。
我们的位置 Windows Presentation Foundation应用程序模型是非常灵活的。它支持标准的和浏览器寄宿的应用程序--它们两者都支持菜单驱动和超链接驱动的导航。此外,应用程序的内容可以被封装到应用程序的部件、被引用的部件或某些位置的松散文件中。总而言之,在Windows Presentation Foundation应用程序模型中建立的用户体验类型仅受个人选择的限制
查看本文来源