1、 Windows Presentation Foundation本书介绍了应用程序的两种主要类型:用户直接运行的桌面应用程序和用户通过浏览器访问的 Web 应用程序。我们在.NET Framework 的两个不同区域创建它们:Windows 窗体和ASP.NET 页面。这些应用程序类型有各自的优缺点。桌面应用程序有较大的灵活性和较强的响应性,而 Web 应用程序可以被许多用户同时在远程访问。在目前的计算环境下,应用程序之间的界限越来越模糊了。有了 Web 服务和 Windows Communication Foundation(WCF,详见第 35 章)后,桌面应用程序和 Web 应用程序都可
2、以用更分布的方式执行,在局域网和广域网上交换数据。另外,Web 客户应用程序(即 IE 或Firefox 等浏览器)不再被看作所谓的“瘦”客户程序,因为它们不再仅仅显示信息。最新的浏览器和运行它们的计算机的功能已经远不止此。近年来,用户体验的个性化已经渐成气候。Web 应用程序现在一般使用JavaScript、Flash、Java 应用程序和其他技术,越来越像桌面应用程序了。例如,Google Docs 的功能就说明了这一点。而桌面应用程序相互连接的趋势越来越明显,其功能从简单(自动更新、在线帮助等)到高级(例如,在线数据源和对等联网),应有尽有。这一点可以通过图 34-1 来说明。图 34-
3、1Windows Presentation Foundation(WPF)是一种统一的技术,利用它编写的应用程序可以在桌面和 Internet 之间搭起桥梁。本章介绍的 WPF 应用程序可以作为桌面应用程序运行,或在浏览器上作为 Web 应用程序运行。WPF 有一个删节版本 Silverlight,可用于给 Web 应用程序添加动态内容。本章将学习 WPF,理解如何使用它创建下一代应用程序,主要内容如下: WPF 的概念 基本 WPF 应用程序的组成 WPF 基础 用 WPF 编程34.1 WPF 的概念利用 WPF(以前称为 Avalon)技术可以编写出独立于平台的应用程序,它清楚地定义了设
4、计和功能之间的界限。WPF 借用并扩展了以前许多技术的概念和类,包括 Windows 窗体、ASP.NET、XML、数据绑定技术、GDI+等。如果读者以前使用.NET Framework 创建过 Web 应用程序,第一次看到 WPF 应用程序的代码时,会立即注意到许多类似之处。尤其是,WPF 应用程序使用标记和后台编码模型,这与 ASP.NET 类似。但在其内部,它们的不同点和相似点一样多,因此 WPF 对于开发人员和用户而言都是一种全新的技术。WPF 开发的一个关键概念是设计与功能几乎完全分开。这样,设计人员和 C#开发人员就可以一起开发项目,其自由程度可以达到以前高级设计概念或第三方工具的
5、要求。这个功能受到所有人的欢迎,包括小型团队、爱好开发的人员、合作开发大项目的大型开发团队和设计人员。下面几节介绍 WPF 给设计人员和开发人员带来了什么好处,使他们能携手工作。34.1.1 WPF 给设计人员带来的好处在 WPF 中,用户界面设计使用的语言是 Extensible Application Markup Language(XAML,读作 zammel)。它类似于 ASP.NET 中使用的标记语言,因为它也使用 XML语法,允许以声明性的、带层次结构的方式把控件添加到用户界面上。也就是说,可以用XML 元素的形式添加控件,用 XML 属性指定控件的属性。控件也可以包含其他控件,这
6、对布局和功能都非常重要。但是,XAML 比 ASP.NET 强大许多,在显示给用户时绝不仅限于 HTML 的功能。XAML 在设计时考虑了目前强大的图形卡,并允许使用这些图形卡通过 DirectX 7 或更高版本提供的全部高级功能。下面列出了这些功能: 浮点数坐标和矢量图提供的布局可以缩放、旋转和转换,且没有质量损失。 高级显示的 2D 和 3D 功能 字体的高级处理和显示 UI 对象的纯色、渐变色和纹理填充,且可以设置透明度 动画故事板功能,可以用于所有情形,包括用户触发的事件,如鼠标单击按钮事件 可重用的资源,以动态设置控件的样式许多功能都专门面向 Microsoft Vista 操作系统
7、,该操作系统可以通过 Aero 接口访问高级图形功能。但是,WPF 应用程序也可以运行在其他操作系统上,例如 Windows XP。如果图形卡因某种原因无法工作,内置于.NET Framework 3.5 运行库中的支持显示系统可以显示 XAML(但有性能损失)。VS 和 VCE 包含创建 XAML 代码并设置其样式的功能,但设计人员选择的工具是Microsoft Expression Blend(EB)。这是一个设计和布局软件包,可用于创建 XAML 文件,接着开发人员就可以使用 XAML 文件创建应用程序。实际上,EB 使用与 VS 和 VCE 相同的解决方案和项目文件,所以可以在这些环境
8、中的一个中创建项目,在另一个环境中编辑它。在EB 中,在编辑后台编码文件时,只需在 Files 窗口(等价于 VS 和 VCE 中的 Solution Explorer)中双击它。图 34-2 显示了 EB 界面。图 34-2在 上可以找到有关EB 试用版本(和 EB2,在编写本书时,它正在开发)的许多信息,并可以下载。但是,编写WPF 应用程序或编辑 XAML 并不需要 EB。图 34-3 显示了图 34-2 中的项目,但该项目在 VCE中加载。图 34-3提示:在 VS 和 VCE 的 2005 和 2008 版本之间有一个区别,它会妨碍 EB 当前版本的使用。但是,可以使用一个配置工具
9、,重新配置 EB,以使用 VS2008 和 VCE 2008。这个应用程序可以从http:/ 上获得。在运行配置程序(BlendConfiguration.exe)时,选择把 EB 配置为使用 Visual Studio代码名 Orcas。这个问题有望在 EB 或 EB2 的未来版本中解决。在 VCE 中,默认屏幕会在两个窗格上显示 XAML(目前不必考虑显示在 XAML 视图中的代码)和这些 XAML 的预览。属性编辑器不太直观,选择对象时,这个编辑器有一些略微古怪的特性。在图 34-3 中,选择了 Label 控件,但选区不完全匹配这个控件。应用程序在两个环境中加载的效果相同,如图 34-
10、4 所示。图 34-434.1.2 WPF 给 C#开发人员带来的好处如上一节所述,开发人员可以创建能在 VS 或 VCE 中工作的项目和解决方案,而设计人员可以在 Expression Blend 中编辑这些项目和解决方案。但与设计人员不同,开发人员主要在 VS 或 VCE 中工作。如本节的引言所述,WPF 使用与 ASP.NET 非常类似的后台编码模型。例如,把 Click 属性添加到表示按钮控件的 XML 元素中,就可以给按钮控件添加一个事件处理程序。这个属性在 XAML 页面的后台编码文件中指定了事件处理程序的名称,事件处理程序可以用 C#编写。注意,还可以在 WPF 应用程序中操作控
11、件,其方式类似于 Windows 窗体应用程序使用编程技术布置用户界面。可以使用后台代码实例化控件,设置其属性,添加事件处理程序,给窗口添加控件,这完全绕过了 XAML。其代码一般比对应的 XAML 声明代码长得多,且无法分隔开设计和功能。编程方式在某些情形下是必须的,但一般应使用 XAML 来布置用户界面上的控件。本章主要从 C#开发人员的角度来阐述。WPF 是一个需要一本书来介绍的主题,所以本章仅简要探讨它。34.2 基本 WPF 应用程序的组成WPF 使用起来很直观,学习它的最佳方式是开始使用它。如果读者已经阅读了本书的其他章节,就会很熟悉许多技术。下面的示例要创建一个简单的 WPF 应
12、用程序,在该示例的说明中,会详细探讨代码和结果,了解如何把代码组合起来。试试看:创建一个基本的 WPF 应用程序(1) 创建一个新 WPF 应用程序 Ch34Ex01,将其保存在 C:BegVCSharpChapter34 目录下。(2) 修改 Window1.xaml 中的代码,如下所示:(3) 双击设计视图中的 Toggle 按钮,如图 34-5 所示,它会隐藏 XAML 视图。图 34-5(4) 修改 Window1.xaml.cs 中的代码,如下所示(双击按钮时,会在toggleButton_Click()事件处理程序中添加 using 语句和新代码):using System;usi
13、ng System.Collections.Generic;using System.Linq;using System.Text;using System.Windows;using System.Windows.Controls;using System.Windows.Data;using System.Windows.Documents;using System.Windows.Input;using System.Windows.Media;using System.Windows.Media.Imaging;using System.Windows.Navigation;using
14、 System.Windows.Shapes;using System.Windows.Media.Animation;namespace Ch34Ex01/ / Interaction logic for Window1.xaml/ public partial class Window1 : Windowpublic Window1()InitializeComponent();private void toggleButton_Click(object sender, RoutedEventArgs e)Storyboard spinStoryboard = Resources“Spin
15、” as Storyboard;if (spinStoryboard != null)if (spinStoryboard.GetIsPaused(this)spinStoryboard.Resume(this);elsespinStoryboard.Pause(this);(5) 执行应用程序,开始、停止、切换动画,示例如图 34-6 所示。(6) 创建一个新的 WPF 浏览器应用程序 Ch34Ex01Web,将其保存在 C:BegVCSharp Chapter34 目录下。(7) 在 page1.xaml 中把元素的 Title 属性的值改为 Color Spinner Web。(8) 打
16、开 Ch34Ex01 应用程序中的 Window1.xaml 文件,把该文件中元素的所有代码复制到 page1.xaml 的元素中。图 34-6(9) 把、和元素改为、和元素(注意,要修改这些元素的开闭标记)。(10) 从 page1.xaml 中删除 5 个元素及其内容。(11) 把 toggleButton_Click()事件处理程序和 System.Windows.Media.Animation 名称空间的 using 语句从 Window1.xaml.cs 复制到 page1.xaml.cs 中。(12) 执行 Ch34Ex01Web 应用程序,结果如图 34-7 所示。图 34-7示
17、例的说明这个示例创建了一个简单的应用程序,得到可以开始或停止的颜色旋转椭圆。但黑白屏幕图不能完全表达这种效果,只有运行代码,才会发现应用程序很漂亮。为了达到这种效果,添加了许多代码,但如果仔细查看代码,就会发现许多代码都是重复的这是必要的,因为要连续改变 4 个椭圆。另外,在后台编码文件中几乎没有添加什么 C#代码,仅为一个按钮添加了代码。以这种方式设计代码说明了两点: 设计人员可以仅用 XAML 代码创建出漂亮的用户界面,其中涉及高级图形功能、动画和用户交互操作。 需要时可以在后台编码文件中完全控制 XAML 用户界面。我们还介绍了如何在 Web 应用程序中使用与桌面应用程序相同的代码。这需
18、要进行几处修改,如后面所述,但基本功能在两个环境中是相同的。这个应用程序中的代码演示了 WPF 的许多特性,介绍了一些关键技术。首先创建桌面应用程序,再看看 Web 应用程序需要的修改。下面列出了桌面应用程序 Window1.xaml 的XAML,以及这些代码的顶级元素:.元素用于定义窗口。一个应用程序可以包含几个窗口,每个窗口都包含在一个单独的 XAML 文件中。但这并不是说 XAML 文件总是定义一个窗口。XAML 文件可以包含用户控件、笔刷和其他资源、Web 页面等。Ch34Ex01 项目中的 XAML 文件甚至定义了应用程序App.xaml。本章后面会介绍应用程序和 App.xaml
19、文件。在 Window1.xaml 中,注意元素包含一些非常简单的属性。其中有两个名称空间声明,每个都用于全局名称空间,其中一个名称空间用于 XML,另一个用于 x 名称空间。这两个名称空间对于 WPF 功能来说非常重要,定义了 XAML 语法的语义。接着是一个 Class属性,它取自 x 名称空间。这个属性把 XAML 元素链接到后台编码文件中的一个部分类定义上,在这个例子中是 Ch34Ex01.Window。这类似于 ASP.NET,一个类用于一个页面,并允许后台代码与 XAML 文件共享相同的编码模型,包括 XAML 元素定义的控件。注意,x:Class 属性只能用于 XAML 文件的根
20、元素。其他 3 个属性 Title、Heighth 和 Width 指定了在窗口标题栏中显示的文本以及用于该窗口的尺寸(单位是像素)。这些属性映射到 System.Windows.Window 类的属性上,Ch34Ex01. Window 派生于 System.Windows.Window 类。System.Windows.Window 类有其他几个属性可以定义额外的功能。许多属性都比元素上使用的属性复杂,它们不只是字符串或数字。XAML 语法允许使用嵌套的元素,为这些属性指定值。提示:在“XAML 语法”一节详细讨论了定义对象、属性和内容的 XAML 语法。例如,下面的代码用一个嵌套的元素定
21、义了 Background 属性:这段代码把 Background 属性设置为 LinearGradientBruch 类的一个实例。WPF 使用笔刷的方式类似于第 33 章讨论的 GDI+。在这个示例中,笔刷定义了一个自上而下从白色过渡到桃色的渐变色。这段代码在嵌套的元素中还定义了另外两个复杂的属性:定义动画的和定义触发器的,它控制动画。这两个属性的功能都远不止此,本章后面会详细介绍它们。在查看这些属性的执行代码之前,需要先看看元素。元素定义了System.Windows.Controls.Grid 控件的一个实例。 这是可以在 WPF 应用程序中用于布局的几个控件之一。它允许用相对于矩形
22、4 条边的坐标来定位嵌套的控件。其他控件可以用不同的方式定位控件。所有的布局控件都在本章后面的“控件布局”一节中介绍。元素包含 5 个元素(System.Windows.Shapes.Ellipse 控件)和 3 个元素(System.Windows.Controls.Button 控件)。这些元素定义的椭圆用于在应用程序中显示旋转图形,按钮用于控制应用程序。第一个元素如下所示:这个元素定义了 System.Windows.Shapes.Ellipse 类的一个实例,用于显示椭圆图形,还设置了这个实例的几个属性,如下所示: Name:用于控件的标识符。 Margin:通过指定图形周围的边距,来
23、指定网格中 Ellipse 控件定义的图形的位置。这些数据在代码中以像素为单位。因此,这个属性根据 HorizontalAlignment 和VerticalAlign ment 属性,映射为图形的实际位置。 HorizontalAlignment 和 VerticalAlignment:指定使用 Grid 定义的矩形的哪一条边布置图形。例如,Left 和 Bottom 值指定图形相对于网格的左下角定位。 Height 和 Width:图形的尺寸。 Stroke:Ellipse 控件定义的图形勾画其轮廓的笔刷。 Fill:Ellipse 控件定义的图形填充其内部区域的笔刷。 BitmapEff
24、ect:显示 Ellipse 控件时使用的特效。Fill 属性使用的笔刷是 RadialGradientBrush。第 33 章的 GDI+代码中有类似的笔刷。在这个例子中,笔刷指定了一个从白色(椭圆的中心)过渡到黑色(椭圆的边界)的渐变色。BitmapEffect 属性设置为使用 BlurBitmapEffect。这是可以应用于 WPF 中图形的几个特效之一。这个特效会用 BlurBitmapEffect.Radius 属性定义的数值模糊图形,它不能应用于 Web 应用程序,因此在第(11)步删除了它。这是桌面应用程序和 Web 应用程序的几个区别之一。代码中的另外 4 个元素非常类似。每个
25、元素都定义了一个颜色连续变化的椭圆,第一个元素如下:这段代码非常类似于上一个椭圆的代码,但有如下区别: Stroke 属性设置为x:null。在 XAML 中,放在花括号中的值称为标记扩展,用于为不能在 XAML 语法中简化为简单字符串的属性提供值。在这个例子中,x:null为属性指定null 值,表示 Stroke 没有使用笔刷。 Opacity 属性指定为 0.5,这表示椭圆是半透明的。 BitmapEffect 属性使用 BevelBitmapEffect,生成倒角,如图 34-5 所示。 指定了 RenderTransform 属性。这个属性设置为带一个 RotateTransform
26、 变换的TransformGroup 对象。这个变换在椭圆连续变化时使用。它指定了一个属性 Angle。椭圆根据这个角度(单位为度)旋转,它最初设置为 0。 RenderTransformOrigin 用于设置中心点,椭圆在进行 RotateTransform 变换时,围绕该中心点旋转。最后两个属性与 XAML 中定义的动画有关,动画是用 System.Windows.Media.Animation. Storyboard 对象定义的。这个对象在中定义,表示 Storyboard 对象可以通过窗口的 Resources 集合使用。这段代码还定义了一个 x:Key 属性,它允许 Storyboa
27、rd 对象使用键通过 Resources 引用:.Storyboard 对象包含 4 个 DoubleAnimationUsingKeyFrames 对象。这些对象可以指定包含 double 值的属性应随时间变化,还可以进一步定义这个操作。这段代码中的每个元素都定义了一个彩色椭圆使用的动画。例如,ellipse1 椭圆的动画如下所示:这里不深入探讨这个元素,但它指定了 RotateTransform 变换的 Angle 属性应在 10 秒内从其初始值变成 360,而且在变换结束后应重复进行。本章的“动画”一节将详细介绍动画。椭圆定义后,有 3 个元素定义了按钮(注意 Click 属性没有出现在
28、示例的代码中,它是在第(3)步双击按钮时由 IDE 添加的):这些元素都指定了 Button 对象的名称、位置和尺寸,所使用的属性与前面的元素的相同。它们还用 Content 属性确定了在按钮上显示什么内容在这个例子中,就是按钮上显示的字符串文本。按钮不仅可以用这种方式显示简单的字符串,还可以使用嵌套的形状或其他图形内容,详见本章的“控制样式”一节。toggleButton 按钮的 Click 属性为 Click 事件定义了一个事件处理方法。这个方法即为 toggleButton_Click(),它实际上是一个路由的事件处理程序。路由的事件详见本章后面的“路由事件”一节。现在只需知道,单击按钮
29、就会触发这个事件,并调用事件处理程序。在事件处理程序的代码中,首先获得 Storyboard 对象的一个引用,该对象定义了动画。如前所述,这个对象包含在 Window 对象的 Resources 属性中,它使用了键 Spin。之后应是检索 Storyboard 对象的代码:private void toggleButton_Click(object sender, RoutedEventArgs e)Storyboard spinStoryboard = Resources“Spin“ as Storyboard;之后,如果前面的代码得到的不是 null 值,就使用 Storyboard.Ge
30、tIsPaused()方法确定动画当前是否暂停。如果是,就调用 Resume(),否则就调用 Pause()。这两个方法可以继续和暂停动画的执行:if (spinStoryboard != null)if (spinStoryboard.GetIsPaused(this)spinStoryboard.Resume(this);elsespinStoryboard.Pause(this);注意,所有这些方法都需要对包含故事板(storyboards)的对象的引用。这是因为故事板本身没有跟踪时间。包含故事板的窗口有自己的时钟,故事板就使用这个时钟。给故事板传送对窗口的引用(通过 this),故事板
31、就可以访问这个时钟。另外两个按钮 goButton 和 stopButton 没有链接到后台代码的任何事件处理方法上。它们的功能是由触发器决定的。在这个例子中,定义了 3 个触发器,如下所示:第一个触发器将 FrameworkElement.Loaded 事件(在加载应用程序时启动)和BeginStoryboard 动作链接起来。这个动作启动 Spin 动画。注意,Spin 动画是通过代码(StaticResource Spin)使用标记扩展语法引用的。这个语法用于引用包含窗口中的资源,它在 WPF 应用程序中很常见。BeginStoryboard 动作的名称是 Spin_BeginStory
32、board,在另外两个触发器中引用,这两个触发器分别链接了 goButton 和 stopButton 的 Click 事件。这些触发器使用了 ResumeStoryboard 和 PauseStoryboard 动作,继续和暂停动画的执行。这段代码在作为桌面应用程序时工作正常,但把它们转换为 Web 应用程序时,需要进行几处修改。实际上,这个示例创建了一个新的 Web 浏览器应用程序,隐藏了几个细节。例如,在浏览器上运行代码有一些安全方面的限制,所以 Web 浏览器应用程序使用了一个临时的键,它用于给应用程序签名。如果希望让应用程序执行浏览器应用程序禁止执行的某些动作,例如,访问本地文件系统
33、,使用临时键就是必须的。还要注意,桌面应用程序的根元素在 Web 应用程序中被元素替换。这是因为浏览器提供的功能略微不同于运行桌面应用程序的主机程序。因此,WPF 使用不同的类表示这些不同的主机。但是如代码所示,许多对象都可以在两个环境中使用相同的代码,详见本章后面的内容。这就完成了这个示例应用程序的分析。这里复习了许多基础知识,还学习了一些新概念,所以现在最好休息一下。本章剩余的内容将深入阐述这里提到的技术,规范需要的语法,再介绍一些新知识。34.3 WPF 基础希望本章第一部分的示例能激起读者对 WPF 编程的兴趣。虽然还有许多新概念需要理解,但我们已经介绍了如何组合 XAML 与.NET
34、 代码快速创建动态的应用程序。还学习了许多功能,包括设计人员不需要 C#的任何知识,就可以设计应用程序的 UI。最后说明了如何用相同的代码创建桌面应用程序和 Web 应用程序。但是,在开始创建 WPF 应用程序之前,应花点时间学习基础知识。本节就介绍 WPF 应用程序的几个基础主题,学习实现它们所需的语法。还要探讨许多可以在应用程序中进一步研究的其他方法。本节的内容如下: XAML 语法 桌面应用程序和 Web 应用程序 Application 对象 控件基础,包括依赖属性、关联属性、路由事件和关联事件 控件的布局和样式 触发器 动画 静态和动态资源34.3.1 XAML 语法本章第一部分的示
35、例虽然介绍了许多 XAML 语法,但没有正式描述它们。这个示例忽略了许多规则和可能性,以便只考虑基本结构和功能。本节将详细介绍 XAML,理解 XAML 文件的组成。1. 对象元素语法XAML 文件的基本结构使用对象元素语法来描述对象的一个层次结构,这个层次结构有一个根对象,它包含了其他所有对象。顾名思义,对象元素语法描述了用 XML 元素表示的对象(或结构)。例如,在前面的示例中,元素用于表示System.Windows.Controls.Button 对象。XAML 文件的根元素总是使用对象元素语法,但在前面的示例中,用于根对象的类不是用元素名(或)定义的,而是用 x:Class 属性定义
36、的。这个语法只能用于根元素。对于桌面应用程序,根元素必须继承于 System.Windows.Window,而对于 Web 应用程序,根元素必须继承于 System.Windows.Controls.Page。用对象元素语法定义的许多对象实际上都是控件,如前面示例中使用的 Button 控件。2. 特性语法在许多情况下,元素用于表示对象(使用对象元素语法)时,就用特性指定属性和事件。例如,前面的元素使用了如下特性:这里的每个特性都设置了 toggleButton 对象的一个属性值,但 Click 除外,因为该特性把一个路由的事件处理程序赋予 toggleButton 的 Click 事件。这些
37、都是特性语法的例子。这里使用的 Name 特性是一个特例:它定义了控件的标识符,以便在后台代码或其他XAML 代码中引用它。特性可以用它们引用的基类和一个句点来限定。例如,Button 控件从 ButtonBase 中继承了 Click 事件,所以可以把上面的代码重写为:注意,这个语法也称为关联属性,参见本章的“控件基础”一节。3. 属性元素语法在许多情况下,需要使用比简单字符串略微复杂的方式来初始化属性值。在示例应用程序中,Fill 属性就是这样,它被设置为各种笔刷对象:.这里属性通过子元素设置,该子元素的名称根据下面的约定来指定: Parent Element Name . Propert
38、y Name 这称为属性元素语法。4. 内容语法许多控件其实都是内容表示器(content presenter)。这表示可以给控件提供内容,这些内容根据控件模板显示出来。例如,为 Button 控件提供的内容显示在按钮表面上。这些内容可以是文本,如示例所示,也可以是图形。使用内容语法可以为控件指定内容,为此,只需把内容添加为表示控件的元素的内容:Go 这行代码用于 Button 控件,以显示文本 Go。这个示例使用了一种比较简单但不太灵活的方式:特性 Content。这里使用的完整内容语法对于较复杂的内容而言是必须的,例如,本节引言中提到的图形内容。给控件使用复杂的内容时,XAML 代码会有复
39、杂的嵌套层次。因此,XAML 允许控件用其他更微妙的方式设置样式。有关内容表示器和样式设置的知识详见“控件样式”一节。5. 合并属性元素语法和内容语法XAML 页面上用对象元素语法格式化的控件可能包括属性元素语法和内容语法。此时,必须遵循下述语法规则: 用于属性元素语法的元素不必是连续的,即一个使用属性元素语法的元素后面可以跟一个使用内容语法的元素,其后的第 3 个元素再使用属性元素语法。 用于内容语法的元素(和文本内容)必须是连续的使用内容语法的文本或元素后不能跟一个使用属性元素语法的元素,其后的第 3 个元素再使用文本或内容语法。因此下面的代码是正确的:Go但是,下面的代码不能工作,因为有
40、两个地方使用了内容语法,它们的中间用一个使用属性元素语法的元素隔开了:DontGo6. 标记扩展前面的示例说明了标记扩展也可以用于属性值例如,值x:null。只要使用了花括号,就是在使用标记扩展。这些标记扩展都可以在特性语法和属性元素语法代码中使用。本章的所有标记扩展都专门用于 WPF。WPF 特有的扩展包括用于引用资源和数据绑定的扩展。34.3.2 桌面和 Web 应用程序本章前面的示例演示了 WPF 应用程序如何作为独立的桌面应用程序和 Web 应用程序运行。前面把 WPF Application 和 WPF Browser Application 项目模板作为起点,添加了 XAML 和C
41、#代码,以完成该应用程序。WPF Application 模板把项目编译为.exe 文件,WPF Browser Application 模板把项目编译为.xbap 文件。提示:XBAP(读作 ex-bap)是 XAML Browser Application 的首字母的缩写,用 WPF 创建的 Web应用程序常常称为 XBAP 应用程序。这些应用程序类型之间的大多数区别都是项目文件(.csproj)之间的区别。Web 浏览器应用程序定义了一些额外的设置,包括两个应用程序的签名和应用程序的清单(如前所述,为了安全起见),以及允许在浏览器中调试的设置。还有一个测试凭证,可用于这个签名。在产品环境
42、下,需要用自己的证书替代这个凭证。在桌面 WPF 和 Web WPF 应用程序之间转换是一个难度很高的过程,因为必须改变许多设置,还要修改一些 XAML 代码,如示例所示。这些改动包括把改为,删除位图效果等功能。最好的方法通常是创建独立的项目,如前面的示例所示。目前最佳的解决方案是由 Karen Corby 实现的,可以从她的博客 http:/ template-flexible-application/上获得。Karen 创建了一个灵活的应用程序模板,可以用于替代默认的 WPF Application 和 WPF Browser Application 模板。这个模板可以使用 IDE上的配置
43、下拉框在桌面应用程序和 Web 应用程序之间切换。如果希望常常在应用程序类型之间切换,这个模板就值得一试,但实际上我们总是要选择一个环境,并一直使用它。图 34-8 显示了如何使用 Karen 的灵活应用程序模板切换到 XBAP 输出。图 34-8必须手工完成的一个任务是禁用不能在 XABP 应用程序(它运行在部分信任级别上)上使用的特性。例如,不能使用位图效果。有关在部分信任的环境下可以和不能使用的完整特性列表,可参见 MSDN 文档中的 Windows Presentation Foundation Partial Trust Security。34.3.3 Application 对象在
44、 WPF 中,大多数应用程序(包括所有的 XBAP 应用程序和使用 WPF Application 模板的桌面应用程序)都包含一个派生自 System.Windows.Application 的类实例。在前面的示例应用程序中,这个对象由 App.xaml 和 App.xaml.cs 文件定义。Ch34Ex01 的 App.xaml 如下所示:元素的语法类似于前面讨论的元素,它以相同的方式使用x:Class 特性,把代码链接到后台代码中的部分类定义上。这段代码定义的对象是 WPF 应用程序的入口。这个对象只能有一个实例,使用静态属性Application.Current 可以通过代码访问它。应用
45、程序的 Application 对象非常有用,原因如下: 它提供了在应用程序的生命周期的特定时刻引发的许多事件,包括前面介绍的LoadCompleted 和 DispatcherUnhandledException,LoadCompleted 在应用程序加载并显示时引发,DispatcherUnhandledException 在抛出一个未处理的异常时引发。 它包含的方法可以用于设置或加载 cookie,定位和加载资源等。 它的几个属性可用于访问应用程序范围内的资源(参见“静态和动态资源”一节)和应用程序中的窗口。Application 对象引发的事件是这个列表中最有用的特性,也是最常用的特性
46、。34.3.4 控件基础WPF 提供了许多可用于创建应用程序的控件。本章简要介绍 WPF 及其功能,所以不详细探讨每个 WPF 控件,而是通过使用它们来学习。下面列出了 WPF 提供的控件:BorderBulletDecoratorButtonCheckBoxComboBoxContextMenuDocumentViewerExpanderFlowDocumentPageViewerFlowDocumentReaderFlowDocumentScrollViewerFrameGroupBoxImage LabelListBoxListViewMenuPasswordBoxPopupProgre
47、ssBarPrintDialogRadioButtonRepeatButtonRichTextBox ScrollBarScrollViewerSeparatorSliderStatusBarTabControlTextBlockTextBoxToolBarToolTipTreeViewViewbox提示:这个列表不包含用于布局的 WPF 控件,这些控件参见本章后面的内容。这里列出的一些控件有非常熟悉的名称,实际上,它们的功能与 Windows 窗体和ASP.NET 应用程序中的对应控件非常类似。例如,Button 控件可用于显示按钮。其他一些控件您可能不太熟悉,所以需要试用,以了解它们的功能
48、。这些控件最初都只有非常基本的外观。为了美化它们,必须给它们设置样式,这会使WPF 的强大功能变得非常明显,如本章后面所述。除了设置样式之外,WPF 控件还使用了其他几个特性。本节将介绍如下内容: 依赖属性 关联属性 路由事件与其他桌面应用程序和 Web 应用程序开发一样,也可以创建自己的控件,而且总是要创建这种控件。在创建控件时,可以使用这里介绍的所有特性。下面几节会举几个例子。1. 依赖属性依赖属性是在整个 WPF 中使用的一种属性,尤其是在控件上使用,它提供了扩展一般.NET 属性的功能。为了说明这一点,考虑某个一般的.NET 属性。在.NET 中创建类时,一般要使用非常简单的代码来实现属性:private string aStringProperty;public string AStringPropertygetreturn aStringProperty;setaStringProperty = value;这里定义了一个公共属性,它以一个私有字段为基础。这些简单的执行代码绝对适用于大多数目的,但除了基本的状态访问之外,没有包含太多的功能。例如,如果要给控件ControlA 添加一个 AStringProperty,让另一个控件 ControlB 响应对该属性的改动,就必须执行如下步骤:(1) 使用前面的代码给控件 ControlA 添加 AStringProp