Longhorn 中最为重要的一个改变是,这种操作系统使应用程序可以一次编码,在多个部署方案中使用。为了实现这个伟大的目标,基于 Longhorn 的应用程序是完全面向对象的,整个应用建立在一个核心对象 Application 之上,该对象提供了运行应用程序所需的所有关键服务。本文将带领读者在一定深度上体验 Longhorn 应用模型,并将它应用于几个基本示例,其中包括经典的 Hello World 应用程序。
Application 对象是 Longhorn 应用模型的核心。通过该对象的一整套属性、方法和事件,您可以使用标记页集合 — 一种增强版本的 HTML — 来编写一致而典型的 Windows 应用程序。Application 对象是 Longhorn 提供的根应用程序对象。它提供了基本的应用程序支持,通常用于那些需要低开销并且不使用页导航和状态管理的应用程序。更复杂的 Longhorn 应用程序将使用密切相关的 NavigationApplication 对象,该对象是从 Application 继承的,但增加了对导航的支持。
一个典型的 Longhorn 应用程序可以视为一组用某些过程代码编写脚本的页。Application 对象控制着程序的执行,并向用户代码产生事件。页使用一种新的声明标记语言来编写,这种语言的代号是 "XAML"(可扩展应用程序标记语言)。利用 XAML 元素,您可以控制每个页的布局,包括文本和图像的显示、插入按钮、文本框等交互式组件。总之,XAML 是用于以声明方式呈现构成应用程序的页的用户界面的语言。当然,除了使用 XAML,您也可以完全使用过程代码来编写 Longhorn 的应用程序。一般来说,一个基于 Longhorn 的成功的应用程序会同时具备 XAML 页和托管过程代码。您可以按自己的方式来组合它们,但这两者的任何组合都是可以接受的。
通过结合使用 XAML 和 C#(或 Visual Basic® .NET)代码,您可以构建各种类型的输出文件,包括传统的 Windows 桌面可执行文件、DLL 库或控制台应用程序。而且,如果您的应用程序足够简单的话,也可以使用独立的 XAML 标记来编写它。这样,在 Longhorn 中就增加了另一种类型的应用程序。只要独立的 XAML 文件不引用代码隐藏类,它就可以在 Longhorn Shell(外壳程序)和浏览器中运行。最后要说明的是,Windows 可执行文件既可以运行在一个窗口中,也可以运行在浏览器中。在这两种情况下,代码保持不变,只要以不同的项目属性重新编译一遍就可以了。
对于 Longhorn,桌面可执行文件是今天的 Windows 窗体客户端应用程序的下一个版本。但从另一方面来说,XAML 以及以浏览器为宿主的应用程序也代表了如今在 Web 上进行客户端编程模型的一次革新。目前,现有的客户端应用程序很少部署到 Web 上。如果您要将 Windows 窗体嵌入浏览器页,原来的特性会有所减少,您还必须对代码做相应的修改。而在 Longhorn 中,通用应用模型使您可以编写一个程序,然后在 Web 上部署。不过,最终的应用程序是特定于 Longhorn 的,与传统的 Web 应用程序(如 ASP.NET)有很大的不同。
当您编译一个应用程序时,代号为 "Whidbey" 的下一个版本的 Visual Studio® 和 .NET 框架(或底层的 MSBuild.exe 工具)会产生一个 .exe 文件、一个应用程序清单(扩展名是 .manifest)和一个部署清单(扩展名是 .deploy)。如果单击 .exe 文件,应用程序会像您期望的那样开始运行。但如果将应用程序设置为在浏览器中运行,则单击 .exe 文件后,会启动 Internet Explorer 的一个实例,并在其中运行该应用程序。还有一种可选的方式是,从远程服务器部署应用程序。完成部署的步骤如下。首先,将部署清单文件复制到服务器的适当位置。可以是 FTP 路径或 HTTP 路径。然后,将编译后的应用程序文件和清单复制到服务器上的适当位置。部署清单、应用程序的文件及清单在服务器端的位置不一定相同。如果它们放在不同的位置,您可以手动编辑部署清单,使它指向应用程序清单的位置。清单文件都是普通的 XML 文件。当用户使浏览器指向指定的部署位置时,Longhorn 会自动下载应用程序及其清单,然后将它们安装在客户端计算机上,同时还创建一个指向 .deploy 文件的快捷方式。最后,用户单击 .deploy 文件就可以运行应用程序。
所有 Longhorn 都有一个共同的结构 — 带有过程代码的 XAML 页(内联或者使用代码隐藏)— 根对象派生于 Application。Application 对象充当控制器;它的生命周期与应用程序的生命周期是一致的。通过 Application 对象,您可以处理高级事件,有时可以在页之间共享代码和状态。它还负责根据应用程序的逻辑让用户在页之间导航。在一个典型的 Longhorn 程序中,用户先执行任务,然后通过从一个页导航到下一个页在应用程序中前进。导航通常通过用新的页替代旧的页来实现。不过,您也可以选择打开一个新的弹出式窗口来显示新的页。导航并不是所有 Longhorn 应用程序所必需的;只包括一个页的简单应用程序就不需要导航。
刚刚提到,除了标记元素,XAML 页还可以包含过程代码。过程代码是必需的,例如,用于处理由页上的一个 XAML 元素产生的事件。过程代码既可以内嵌到 XAML 文件的正文中,也可以放在单独的代码隐藏文件中。
Longhorn 中的编程基于托管代码。不过,只有几种与 .NET 兼容的语言可用于编写基于 XAML 的应用程序。目前,这类语言包括 C#、Visual Basic .NET 和 JScript® .NET。到 Longhorn 发布时,其他与 .NET 兼容的语言也将包括进来。目前只能使用这三种语言的原因是,XAML 文件的源代码必须即时分析和编译,因此,编译器和相关的文档对象模型必须提前就绪。不过,需要注意的是,如果完全使用过程代码编写应用程序,您可以使用任何与 .NET 兼容的语言,它们都具备有效的编译器。注意,基于 XAML 的应用程序只能使用这三种语言。如果 XAML 页中嵌入了过程代码,那么您必须先编译应用程序,然后才能运行它;如果 XAML 页中没有过程代码,那么双击就可以显示它,就像 HTML 网页那样。Longhorn 不处理未编译的代码,也不能即时编译代码。您一定迫不及待地希望看到 Longhorn 版本的 "Hello World" 应用程序是什么样子吧? 下面是一个您可以编写的最简单的 XAML 代码的示例:
< Canvas
xmlns ="http://schemas.microsoft.com/2003/xaml"
Background ="LightCyan"
Width ="100%" Height ="100%" >
< Image Source ="lh.bmp" Canvas.Left ="5" Canvas.Top ="5" />
< Text Canvas.Left ="90" Canvas.Top ="20" FontSize ="36" > Hello, Longhorn!
</ Text >
</ Canvas >
< Canvas
xmlns ="http://schemas.microsoft.com/2003/xaml"
Background ="LightCyan"
Width ="100%" Height ="100%" >
< Image Source ="lh.bmp" Canvas.Left ="5" Canvas.Top ="5" />
< Text Canvas.Left ="90" Canvas.Top ="20" FontSize ="36" > Hello, Longhorn!
</ Text >
</ Canvas >
图 1 简单的 XAML 页
将这段代码保存在文本文件中并用 .xaml 作为扩展名,然后用 Longhorn 浏览器指向该文件或者在外壳程序中双击该文件即可。图 1 显示了结果。<Canvas> 节点定义了应用程序的用户界面区域 — 基本上,相当于画布。Background 属性指定了该区域的背景色,Width 和 Height 则指定了表面的大小。<Image> 和 <Text> 元素定义了该页的内容。这两个元素使用父级 Canvas 对象的 Left 和 Top 属性定义了它们的绝对位置。
Longhorn 提供了一组扩展和增强 .NET 框架 1.1 的框架类。Longhorn 中的扩展包括:XAML 支持、存储系统、应用模型、可信计算和高级 Web 服务等。在 Microsoft 的路线图计划中,Longhorn 和 Whidbey 代表了两个截然不同的里程碑。Whidbey 预期将比 Longhorn 提前几个月发布,因此,当 Longhorn 发布时,将包括 Whidbey 的升级版本,以提供 Longhorn 中的核心服务。
我们再来看看所有 Longhorn 应用程序共同的基础 — XAML 语言。XAML 语言是一种基于 XML 的语言,它专门用于描述应用程序的用户界面。熟悉 Win32® 和 .NET 框架的程序员可以看出 XAML 标记和传统的 Windows 控件之间明显的相似之处。不过,相比全套 Win32 公共控件或 Windows 窗体控件,XAML 标记更抽象,范围也更广。为了迅速理解 XAML 代表的含义以及您应该怎样看待 XAML,可以想想 ASP.NET 页。或者,更具体一点,您可以想像某个只有服务器端控件 (runat="server") 的 ASP.NET 页,就无需用文字解释了。
每一个 XAML 标记都对应一个 .NET 框架类,并包括许多方法、属性和事件。您可以直接在 XAML 脚本中以声明的方式来设置属性和编写事件,也可以使用封装到代码隐藏类的过程代码。隐藏在每个标记后面的控件在运行时进行实例化,并得到屏幕上的一个区域,以便在上面呈现其输出。从最高的抽象级别来看,该模型非常像 ASP.NET,只不过这个模型经过抽象后,可用于一般的、更丰富的 Windows 平台。
XAML 中的每个元素都使用一个基础类,但许多类没有对应的 XAML 元素。这些类常常是抽象类,它们大多被用于继承。用 XAML 创建的任何东西都可以用过程代码来创建。例如,要用 XAML 创建一个按钮元素,可使用下面的代码:
< Canvas xmlns ="http://schemas.microsoft.com/2003/xaml" >
< Button Canvas.Left ="10"
Canvas.Top ="10"
Width ="90px"
Height ="32px" > Click Me </ Button >
</ Canvas >
也可以用下面的 C# 过程代码创建一个一模一样的按钮:
Button btn = new Button();
btn.Width = new Length( 90 );
btn.Height = new Length( 32 );
Canvas.SetTop(btn, new Length( 10 ));
Canvas.SetLeft(btn, new Length( 10 ));
btn.Content = " Click Me " ;
在 XAML 文件中,您不能使用方法,但可以设置属性 (attribute)。XAML 中的 attribute 通常与类的 property(属性)相对应,但类的几个 property 并没有对应的 XAML attribute。您可以逐个设置类属性或使用样式表。样式表是样式属性的集合,编译器会将它自动应用到使用该样式表的控件。所有 XAML 页都至少有一个 panel 元素,它充当窗体容器,并控制子内容的位置以及背景色和字体等全局属性。XAML 页的元素是以层次结构组织的,只有一个根元素。通常,这个根元素是 Panel 派生的类,如 DockPanel 或 Canvas,或是 Decorator 派生的类,如 Border。Canvas 元素用于根据绝对坐标来确定内容的位置。一般来说,每个元素被绘制在不同的位置;如果两个以上的元素被绘制在相同的坐标上,元素在标记中出现的顺序将决定它们的绘制顺序。
除了属性和方法,XAML 元素还支持许多事件。通过创建事件处理程序,您可以使页动态地响应各种通知。创建 Longhorn 事件处理程序并关联到元素的方法与您如今在基于 .NET 的应用程序中使用的方法几乎是一样的。事件处理程序通过一个属性来声明,然后,处理程序通过代码隐藏类集成到应用程序中:
< Button Width ="90px" Height ="25px" Click ="OnHandleClick" >
Click Me
</ Button >
页中的元素形成了应用程序树,这是一个包括了应用程序的所有运行时组件的对象模型,并且可以通过代码访问。每个控件都提供了自己的访问子级的方法。主要模式有:panel.Children.Add(element)、listbox.Items.Add(object)、button.Content = object 和 textbox.TextRange.Text = string。
支持子级的控件还支持作为子级的任意对象 — 字符串、元素,或某个完全随机的对象(在这种情况下可调用 ToString)。每个控件都有一个 Parent 属性,用于访问它在树中的父级。注意,访问该树的各个属性只返回直接的子级,而不是子代。利用对象模型,您可以自如地操纵页上元素的每个方面。它还提供了一些无法通过 XAML 实现的额外功能。例如,您可以动态地创建元素,但还可以根据运行时条件即时创建元素。
为了更清楚地说明这一点,我们来看第二个应用程序示例。正如前面提到的,任何规范的 Longhorn 应用程序都包括过程代码以及 XAML 脚本。我们将讨论一个比最初的 Hello World 应用程序更复杂的情况,并且处理一些事件。
图 2 显示了一个新的应用程序的所有源代码,其中,XAML 被绑定到代码隐藏类中存储的某些过程代码。如果将 XAML 解决方案与 ASP.NET Web 窗体页进行比较,除了语法不同之外,您很难发现任何明显的区别。def:CodeBehind 和 def:Class 属性封装 C# 代码的方式与 Visual Studio 代码隐藏类封装 ASP.NET 代码的方式也非常相似。我们来命名图 2events.xaml 中的 XAML 文件,并且命名也出现在图 2events.xaml.cs 中的代码隐藏类。如果您将浏览器指向该文件,就会产生一个运行时错误,如图 3 所示。该错误消息在我使用的内部版本中不是很清楚,但错误的原因非常明显。该错误一定与 XAML 代码不能被动态编译有关。与 ASP.NET 不同的是,Longhorn 要求您显式编译任何嵌入的或只是由 XAML 脚本引用的过程代码。这一特性可能会在未来的内部版本中有很大的变动,但在目前,它使您不得不在 Visual Studio 中管理 Longhorn 项目文件。或者,您的另一个选择是,去熟悉 MSBuild,它的可执行文件是 msbuild.exe。Longhorn 解决方案和项目文件的扩展名和结构与 Visual Studio 2003 中是一致的。Longhorn 项目文件大体上如图 4中列出的代码所示。
图 3 XAML 错误
项目文件包含有关项目的重要信息。<Project> 标记指明了编译器必须生成的包的类型。项目属性和项集中在嵌套标记中 — <ProjectGroup> 和 <ItemGroup>。每个元素的名称都很易于理解。项目文件在命令行中被传递到 msbuild.exe 实用程序。MSBuild 驻留在 Windows\ Microsoft .NET 下的 Framework 文件夹中。下面的简单命令生成一个基于 Events.proj 项目文件的可执行文件:
msbuild.exe Events.proj
除非您希望实践一下 MSBuild 工具,否则,您不必过于关注它。前面提到,Whidbey 版本的 Visual Studio 与 Longhorn SDK 高度集成,只要单击工具栏按钮或按一下 F5 就可以构建 Longhorn 应用程序。图 5 显示了图 2中的代码的运行效果。在应用程序树中,只有一个上面有文字的蓝色面板是可见的。应用程序树其实比您在图 5 中看到的要稍微复杂一些。根节点是一个包含 DockPanel 对象的窗口。DockPanel 是所有用户界面元素最外层的容器,它包含一个子级 — FlowPanel,FlowPanel 又包含一个 Text 对象。文本由 Text 类的一个实例来表示。您应该已经注意到,XAML 标记有几个属性用于控制其显示。更重要的是,这些标记支持 ID 属性,ID 属性提供了运行时标识。只要您在被分配给控件的区域内单击鼠标,与 FlowPanel 类相关联的 MouseLeftButtonDown 事件就会触发。此时,将调用 OnLeftMouseDown 事件处理程序代码。流动面板的宽度和高度变成了原来的两倍,文字也随之改变。再次单击鼠标,面板又恢复为原来的大小。
图 5 运行应用程序
虽然您也可以完全使用过程代码编写应用程序,但 XAML 文件使应用程序用户界面的描述和模板化简单得多。
XAML 语言由四个主要类别的元素构成 — 面板、交互控件、与文档相关的元素以及图形。Border(边框)元素虽然不属于上述四个类别中的任何一类,但它也很重要。从技术角度来说,它相当于负责细节布局的“装修工人”,只包含一个子级,但添加了绘制效果。在 XAML 中,面板负责页面布局,并充当其他元素的容器。面板是管理所包含的元素的绘制、大小和位置、内容安排的组件。Longhorn 中有六个内置的面板类,但开发人员可以用自定义绘制行为来创建自己的面板。预定义的面板包括:Canvas、DockPanel、FlowPanel、GridPanel、Table 和 TextPanel。图 6 中描述了这些面板类型。
Canvas 面板是预定义面板中唯一支持显式定位的。它所有的子元素从一个固定的位置开始绘制,延长指定的尺寸。如果两个元素重叠,后绘制的那个将覆盖下面的区域。注意,坐标是相对于画布的,这意味着,(0,0) 表示区域的左上角像素。下面是一个画布对象的示例:
< Canvas xmlns ="http://schemas.microsoft.com/2003/xaml"
Height ="600" Width ="800" >
< Border Background ="red"
Canvas.Top ="0px" Canvas.Left ="0px"
Height ="100px" Width ="100px" />
< Border Background ="green "
Canvas.Top ="100px" Canvas.Left ="100px"
Height ="100px" Width ="100px" />
< Border Background ="blue"
Canvas.Top ="50px" Canvas.Left ="50px"
Height ="100px" Width ="100px" />
</ Canvas >
DockPanel 组件以不同的方式发展出相同的包容概念。这种面板的所有子级都水平或垂直地相互停靠在一起。在这种情况下,定位是相对于前一个控件的,并且根本不需要坐标。DockPanel 是创建显示效果像工具栏这样的按钮条的理想选择。下面是一个示例:
< Border xmlns ="http://schemas.microsoft.com/2003/xaml"
Background ="black" >
< DockPanel >
< Button DockPanel.Dock ="Left" Width ="50px" Height ="32px" >
Open
</ Button >
< Button DockPanel.Dock ="Left" Width ="50px" Height ="32px" >
Save
</ Button >
< Button DockPanel.Dock ="Left" Width ="50px" Height ="32px" >
</ Button >
</ DockPanel >
</ Border >
第一个按钮位于面板的最左侧,其他每个按钮都按水平方向排列在前一个按钮的旁边(请参见图 7)。
图 7 按钮布局
FlowPanel 元素使子组件以各种方向在可用的区域中流动。该面板还包含可处理内容超出面板宽度的情况的逻辑。在这种情况下,根据对象的配置,超出部分的内容可以折叠显示在下一行或被截掉。下面的 XAML 示例说明了 FlowPanel 如何中断内容并使内容折行显示(这种内容在逻辑上已经设计为适合单个行)。在这个示例中,四个方形的元素包含在一个更大的元素中。这四个方块的宽度之和大于容器的宽度。因为所有小方块不能在一行显示,这四个方块被截断,最后一个方块在第二行显示。注意,该面板默认的方向是:从左到右,从上到下:
< FlowPanel xmlns ="http://schemas.microsoft.com/2003/xaml"
Width ="250px" Height ="250px" >
< Border Background ="red" Width ="75px" Height ="75px" />
< Border Background ="green" Width ="75px" Height ="75px" />
< Border Background ="blue" Width ="75px" Height ="75px" />
< Border Background ="orange" Width ="75px" Height ="75px" />
</ FlowPanel >
GridPanel 是一个开销不大的元素,它用类似网格的方式安排对象的布局,从而构建出一个相对比较简单的表。通过 GridPanel,您可以在行和列上任意放置控件,构成一个矩阵,但总的来说,它的功能还是很有限的。要构建具有高级功能的更复杂的表,您可以使用 Table 面板。Table 面板的结构是由多组行构成的。您可以为页眉或页脚定义一组行,还可以插入多组具有不同设置的行。利用该面板的布局功能,您可以为数据创建非常复杂的表格式表示形式。
最后,如果需要复杂的文本布局支持,TextPanel 是一个理想的选择。不过,对于基本的文本支持,Text 元素是一个更好的选择,因为它的开销更小(当然,功能也更少)。
MSAvalon.Windows.Controls 命名空间集中了所有负责用户交互的用户控件。属于此命名空间的类都是您很熟悉的类:Button、ComboBox、ListBox 和 CheckBox,但也有一些新成员,如 ContextMenu、PageViewer 和 RadioButtonList。用户交互式元素的基类是 Control,它提供了一组公用的属性和方法。
图 8 页面查看器
比较特别的是,PageViewer 控件提供了用于查看在线文档的用户界面,并且包含分页和页导航功能。下面的示例生成了一个页面查看器(如图 8 所示),它占据了整个工作区。该页面查看器将 PageViewer 标记的 Source 属性中指定的文件中的文本分页并显示出来:
< DockPanel ID ="root" xmlns ="http://
schemas.microsoft.com/2003/xaml"
xmlns:def ="Definition" >
< Text DockPanel.Dock ="Top" > See a
PageViewer control in action below.
</ Text >
< PageViewer DockPanel.Dock ="Top"
Source ="Sample.xaml" Height ="80%" Width ="80%" />
< Button DockPanel.Dock ="Top" Width ="50%" > OK </ Button >
</ DockPanel >
有意思的是,通过 PageViewer 控件查看的源文件不能是纯粹的文本、RTF 或 HTML。至少在目前,它必须是包含无格式 ASCII 文本、由支持分页的 Avalon 控件封装的 XAML 文件。Avalon 是 Longhorn 中新的表示子系统的代号(请参见 Charles Petzold 在本期撰写的文章)。例如,我们来看看 TextPanel 类:
< TextPanel ID ="root" xmlns ="http://schemas.microsoft.com/2003/xaml" >
text to show goes here
</ TextPanel >
将上面的代码片断保存到 sample.xaml,以便运行前面的示例。记住,xmlns 命名空间是必需的。
MSAvalon.Windows.Documents 命名空间中的类负责文档的显示。这些类合在一起,看上去像是高级 HTML 标记的超集。其中包括的类有:Block、Column、Heading 和 Footer,以及 ColumnGroup、RowGroup、HyperLink、List 等等。
您在前面看到的 Text 类属于这个预发布版本的 Longhorn 的 System.Windows.Controls 命名空间。Text 类支持多行和各种文本格式设置规范,包括粗体和斜体、字体大小,以及嵌套子元素。在所需文本相对比较简单的情况下,Text 就是理想的类,因为它开销较小,功能全面。 Longhorn 表示模型通过使用 XAML 面板元素来绘制图形。面板提供了许多大小和对齐方面的属性,并且可以控制边界、背景色和填充。
Longhorn 使用 Windows 矢量图形 (Windows Vector Graphics) 来绘制图形内容,它与 GDI 和 GDI+ 相比有许多优点。Windows 矢量图形是一个基于 XML、便于使用和重用的图形标记系统。如果您是可缩放矢量图形的爱好者,您一定也会痴迷于 Windows 矢量图形。Windows 矢量图形提供了预定义的形状,包括 Ellipse、Line、Path、Polygon、Polyline 和 Rectangle,它们都是从 Shape 类继承的。这些元素从 Shape 那里继承了许多共有的属性,包括 Stroke 和 StrokeThickness,以及 Fill,再加上一些用于指定坐标和顶点的属性。通过进行变形,形状可以扭斜、旋转、平移和缩放。除了可以为形状指定纯色填充色和背景,您还可以指定渐进色。下面的示例将水平渐进色设置为一个 Rectangle(矩形)形状的 Fill 属性,将 Red(红色)设置为起始色,将 Blue(蓝色)设置为最终过渡到的颜色:
您可以使用 XAML 语言迅速有效地构建一个用户界面的原型。我敢保证,到 Longhorn 发布时,许多开发工具将会更新(或从新构建),以完全支持具有所见即所得特性的 XAML。Longhorn 的技术预览版已经包括了一些到 Whidbey 版本的 Visual Studio .NET 的扩展。下面我们来看一个更规范的交互式应用程序。您很快就会发现,这与编写 Windows 窗体应用程序并没有太大的不同,而且,您将惊喜地发现,您目前已经掌握的 .NET 技巧可以很好地应用于 Longhorn。如图 9 所示,您可以看到一个很小的应用程序示例的用户界面,界面中包括一个文本框和一个上下文菜单。文本框是使用 XAML 定义的;上下文菜单是动态创建的。该应用程序的全部源代码显示在图 10 中。
图 9 上下文菜单
在 Longhorn 中,所有应用程序都由 Application 类的一个实例来表示。此对象是 Longhorn 应用模型的核心。不过,Application 对象只提供基本的应用程序支持,通常只有那些需要低开销并且不使用导航功能的应用程序才使用该对象。实际上,大多数 Longhorn 应用程序使用密切相关的 NavigationApplication 对象,该对象是从 Application 继承的,并增加了对导航的支持。
NavigationApplication 对象支持各种方法、属性和事件,它们使您能够将大量 XAML 页组合到一个应用程序中。在某种意义上,基于更简单的 Application 类的 Longhorn 应用程序是可以与基于对话框的 Win32 应用程序进行比较的。同样,您可以将支持导航的应用程序与完整的 Win32 应用程序进行比较,在这样的 Win32 应用程序中,各种窗口和窗体共同形成了应用程序的整体功能。下面的代码显示了这种导航功能的示例:
myApp = NavigationApplication.Current;
win = (Navigation.NavigationWindow) myApp.Windows[ 0 ];
•••
private void Button_Back(Object sender, ClickEventArgs e)
{
// If possible, go to the previous window
if(win.CanGoBack())
win.GoBack();
}
您可以通过使用 Application(或 NavigationApplication)导航对象上的静态属性 Current 获得对应用程序对象的引用。前面的示例还说明了如何获得对堆栈中第一个窗口的引用。Button_Back 事件处理程序检查前一个窗口对象是否存在,如果存在,就以一种类似浏览器的方式跳回它那里。
下一个示例将说明如何通过派生一个新的类并覆盖 OnStartingUp 方法来定制导航应用程序启动时的行为。下面的代码片断覆盖了 OnStartingUp,在应用程序启动时创建并显示一个导航窗口:
public class MyApp : NavigationApplication
{
NavigationWindow win;
•••
protected override void OnStartingUp(StartingUpCancelEventArgs e)
{
win = new NavigationWindow();
// Add elements to the window
•••
navWin.Show();
}
•••
}
让我们再回到图 9 中显示的应用程序示例。根据我刚刚讨论的理由,您可以使用 Application 或 NavigationApplication 作为基类,因为该应用程序将使用一个单窗口、基于对话框的应用程序。正在运行的、名为 Sample1 的类覆盖了 OnStartingUp,并定义了好几个事件处理程序。覆盖 OnStartingUp 方法是必需的,因为它表示应用程序启动的初始化步骤,因此,是在窗口打开之前执行您自己的操作的理想地点。
在 OnStartingUp 中完成的操作就是窗口的创建。窗口的内容在 XAML 文件及其代码隐藏类中描述。XAML 页本身是由控件和其他组件构成的,它们被组织在一个具有层次结构的树中。正是这些不同的组件(称为元素)之间的关系在很大程度上说明了页如何呈现和表现。图 9 中的示例页包括一个 TextBox 控件,该控件具有给定的大小和字体。这个文本框被绑定到 ContextMenuEvent 事件。只要用户右击控件的工作区,就立即触发该事件。
事件处理器将创建一个 ContextMenu 对象(请参见图 10)。上下文菜单经过填充、以图形方式配置后,就绑定到它的父对象 — TextBox。毫无疑问,在 Longhorn 中,通过选择背景色和前景色、边框和字体,您可以轻松地自定义菜单的外观(以及所有控件的外观)。如果您认为以前版本的 Windows 和 .NET 框架也有可能完成同样的任务,那您就错了。在 Win32 中,这种自定义需要许多编程工作,一点也不简单。而在 .NET 框架中,包装类封装了必要的代码,只留下很少的属性供用户控制。另一方面,在 Longhorn 中,控件的用户界面就像它看上去那样简单。
上下文菜单的元素是从 MenuItem 类创建的。使它们触发事件处理程序的方式几乎与基于框架的应用程序完全一样:
mia = new MenuItem[ 3 ];
for ( int i = 0 ; i < 3 ; i ++ )
{
mia[i] = new MenuItem();
cm.Items.Add(mia[i]);
mia[i].Foreground = Brushes.Black;
}
mia[ 0 ].Header = " Lower Text " ;
mia[ 1 ].Header = " Upper case " ;
mia[ 2 ].Header = " Select all " ;
mia[ 0 ].Click += new ClickEventHandler(LowerCase);
mia[ 1 ].Click += new ClickEventHandler(UpperCase);
mia[ 2 ].Click += new ClickEventHandler(SelectAll);
当某个菜单项被单击后,立即执行事件处理程序,并使用传递的第一个参数来检索对其源对象的引用:
public void LowerCase(Object sender, ClickEventArgs args)
{
MenuItem mi = (MenuItem) args.Source;
ContextMenu menu = (ContextMenu) mi.Parent;
TextBox thisTextBox = (TextBox) menu.PlacementTarget;
thisTextBox.Text = thisTextBox.Text.ToLower();
}
为了检索 TextBox,您必须在树中向上追溯。首先,到达 MenuItem,然后是 ContextMenu,最后,您就可以访问其所有者 TextBox 了。此时,修改 TextBox 的内容显得轻而易举。