如果经历过 .NET 的 1.0,1.1 以及 2.0 版本,你就很可能发现 .NET 3.0 中的 WPF 区域中的一些文档有点不同。具体来说, WPF 负责介绍几个 CLR 和托管代码封装方面的新概念。 WPF SDK 团队为在参考资料中展示这些新概念而做的努力是很大的进步,主要致力于改变,因为其它的技术也在它们的 API 中采用了相同或者类似的范例。
System.Reflection 和 .NET 3.0 SDK
微软用于创建托管 SDK 框架的 “ 魔法 ” 其实就是映射进程(典型的就是采用托管映射,但有时也有非托管的映射,这取决于 API )。编译工具映射所有 .NET 3.0 的 API , SDK 作者就在纲要中填充映射告诉我们的关于 API 的消息(至少这是一个目标)。但是对于比 System.Reflection 本身更新 的编程概念的映射, System.Reflection 做得不是特别好。你可以通过查看定制特性来得到一些更具体的信息。但对于那些像 XAML 一样的语 言,或者独立属性,由于 System.Reflectio 从 1.1 开始就没有本质的改变,所以不能为 3.0 提供好的 API 。如果你要编写自己的映射,你不 得不做些额外的创造工作(我不会在这里描述这个问题,因为太复杂了,而且超出了本主题的范围)。对我们的 SDK 开发团队来说,他们也不得不非常熟悉映射代 码。然后,其他的各种各样的人,包括一些像我们这样的程序员不得不跟上表示策略 —— 怎么在更好,更标准的托管代码段文档中表示额外的信息,比如异常和返回 值等。 0000
正在讨论的隐藏的秘密是参考主题部分,这部分是大量的设计会议和开发工作的结果。每一个隐藏秘密都代表着编程的一个具体方面,和 WPF 非常相关并且对 WPF 来说是新内容。所有的信息都在 SDK 页面,等待你的发掘。但是,我们还没搞清楚这些突然在 WPF 参考文档中出现的额外段落是什么,以及他们意味着什 么。
抛开这些吧。让我们来照亮这些 WPF 文档中隐藏的秘密。一切都会得到解释!
独立属性
很多时候会被问道一个问题 “ 独立属性到底是什么? ” 或者一个相关的更尖锐的问题 “ 我为什么要去关心一个东西是不是独立属性呢? ” 我们的答案是:独立属性总览。
跨过这个概念上的障碍之后,下一个逻辑上的问题可能是 “ 好吧,独立属性就是一种支持 CLR 属性的方式,那我该怎么分别那些属性是通过这种方式被支持的呢? ” 我们遇到的第一个隐藏秘密是:独立属性,和独立属性信息段。
在文档中提到两种方法来判断某个给定的属性是否是独立属性:
1 )当你在浏览任何类型的成员表,并且看到公共属性,读描述的时候。如果这个属性是独立属性,那么描述的最后一句就是 “ 这是一个独立特性。 ”
2 )假设你在说明 CLR 属性的 SDK 的主题页面。同样,在成员表的相同的描述中,你可以找到这句话 “ 这是一个独立属性 ” 。除了描述外,每一个独立属性的属性页面内还有一段可以被合适地成为独立属性信息段。这一段在表格中包含两点:
一个到含有独立属性标志符的域的链接。很多属性系统 API 函数都需要这个标志符以执行一个属性或者获得关于它的信息。有人说,对那些只是获取或设置某个已 经存在的具体属性的应用程序编程来说,使用属性系统 API 并不总是必须的,因为你可以调用更简单的独立属性 CLR“ 封装 ” 。这样,标志符主要是和包含独立 属性的高级编程任务相关,比如处理属性元数据,或者追踪属性值。
如果一个属性是框架层的属性,那么独立属性信息段会把元数据中 “ 标记 ” 为 true 的列出来。当独立属性被 WPF 框架层解释时,标记报告独立属性的某些共同 特征,尤其是子特征,比如,整体系统,数据绑定,或属性值继承。 SDK 报告这些标记,因为这有助于实例了解改变属性是否会强迫修改整体设计,或是否你可以 省略指定绑定的两个状态,因为这是独立属性的默认设置。
默认值
横跨独立属性和 “ 常规 ” 属性的一个概念是默认值。一般来说, .NET 文档会在属性值段告诉你属性的默认值,并且为保持一致性,这也是默认属性被报告给独立 属性的地方。但是,独立属性默认值实际上来自属性元数据,尽管在一个平面上 CLR 属性可能来自一个类或者是拓展执行。这样,通过重写属性元数据,独立属性 就可以被子类或新的属性拥有者轻易改变。在已有的 WPF 独立属性中偶尔会发生这种情况。当发生时,每一个类的重写都会被记录在 Remark 段。
Routed 事件
在你不可避免地问 “ 什么是 routed 事件? ” 之前,可以去这里看看 Routed Events Overview 。现在我们解决这个问题了。
不 像独立属性, routed 事件没有像 “ 这是一个 routed 事件 ” 之类的习惯性描述。基本元素( UIElement, FrameworkElement, ContentElement, FrameworkContentElement )有很多的 routed 事件:可能它们的事件有 75% 都是 routed 的。其它的类,像控件也会有少数的 routed 事件,也许和着一些不 route 的标准 CLR 事件。为了区分一个事件是否 route ,你需要在事件主题看看是否有 Routed 事件信息段。
Routed 事件信息段是一个有如下三项的表:
* 一个指向包含有 routed 事件标志符的域的链接。和属性系统 API 以及独立属性标志符一样,事件系统 API 也需要这个标志符以接入事件。像增加或移除句柄一类的简单操作并不是必须 标志符,这也是因为 routed 事件会有一个 “ 包装 ” ,这样就可以支持 CLR 语言中增加 / 移除事件句柄的 CLR 语法。但,如果直接使用 AddHandler 或调用 RaiseEvent ,就需要知道 routed 事件的标志符。
*Routing 策略。有三种可能: Bubbling , Tunneling ,和 Direct 。这儿有个小秘密:如果之前查看过事件名字,那么 routing 策略就总是 Tunneling ,这是一个惯例。但区分 Bubbling 和 Direct 就有点困难了,因为没有不同的命名习惯。所以,我们提 供了参照页的信息。 Direct 事件实际上没有在它的元素之外 route ,但他们仍然服务于 WPF-specific 目的,在这里有描述 Routed Events Overview 。
* Delegate 类型。你可以在声明事件的语法中找到列出的 delegate 。我们在这里重复是为了方便,所以你可以直接跳到 “ 写恰当的句柄 ” 的地方。
附带属性
严格地说,附带属性是 XAML 的概念,而不是 WPF 的。但 WPF 是第一个发行的采用了 XAML 语言的工具。所以 WPF 是用文档来描述 XAML 语法和 XAML 语言的先锋。如果你是代码编写员,并看了某个附带属性的叫做 DockPanel.Dock 的文档,就会注意到一些特别的东西:没有 XAML 语法,没有代码语法。如果浏览 DockPanel 类,去看映射或对象就可以 证实:对 CLR 没有类似于 “Dock” 属性的东西。甚至为了在其它的产生映射的参照中使这个页面存在, SDK 开发团队不得不注入这个页面。然而,对我们这 些在标记和代码间可以随意转换的人来说,附带属性的语法段确实提供了一个到附带属性的 “ 真实 ” 代码 API 的链接,这可以成为一系列获取和设置的接入方法。 对 DockPanel 类来说有 GetDock 和 SetDock 。(不幸的是,在 MSDN 版本的这些页面中似乎没有这些链接;在 Visual Studio 和 offline Windows SDKs 中有 … 相信我 ... )
附属事件
类似地,这是一个 XAML 的概念,但在 WPF 得到具体的应用。 XAML 语法,但对同等代码接入来说是一个 “ 秘密 ” 接入方法。在附属事件情况下,这些方法的 类型是 Add*Handler 和 Remove*Handler 。例如, Mouse.MouseDown 调用 AddMouseDownHandler 就可以 有附带到给定的 UIElement 实例的句柄,并可以通过 RemoveMouseDownHandler 移除。对实际应用来说,附属事件不一定那么重要, 因为大多数都是被 UIElement 以更方便的方式重新使用而已。但如果你在写一套相关的控件,或者在实现一个服务,那么附属事件就很可能进入你的视野。
XAML 语法
SDK 文档提供了 XAML 各种用途下的 XAML 语法,比如用来实例化一个 XAML 类的对象元素标签,用来设置属性或附带事件句柄的属性,还有 XAML 的具 体概念,如 content property 或 property element 语法。另一个隐藏的秘密来了 ... 好的,也不是那么的隐秘了,因为我以前已经在博客上写过了。 XAML 不一定非要被绑定到计划中。最终决定 XAML 是否有效的是 XAML loader ,最终成功还是失败取决于一些加载动作,比如试图在组件中找到一个命名匹配的类型,调用它的默认 ctor ,以及返回一个实例。或者,一旦对象 存在,就是试图找到一个匹配属性名字的 property ,然后试图用属性值的 string-to-X 变换,或者甚至是更复杂的在 property 元素中以 子关系存在的 XAML 的什么东西的封装来填充它。对于保持 XAML 的扩展性来说是有必要的,但是这会让编写文档和静态的 XAML 词典更困难一点儿。其一, parser 会允许你实例化一个在对象模块中没有 “ 家 ” 的元素。比如,大多数 EventArgs 子类可以在 XAML 中建立,因为它们可以方便的拥有一个默 认 ctor (加入 XAML 俱乐部是如此容易 .... )但是,然后呢?从狭义来说, XAML 是 UI 定义上的。广义上讲, XAML 是相关的对象和你希望建立的 他们的属性的任何结构的定义。你不能把 EventArgs 作为任何具有可疑资源收集异常的东西的子元素。这让 XAML 文档进退两难:这是 XAML 吗,或者 不是?这个困惑经常出现。一般来说,我们用具体情况来说明 XAML 。有什么原因让你希望用 XAML 来实例化这个类吗?如果有,我们会给你看一个语法。如果 没有,就像 3.0 的 EventArgs 子类一样,我们会告诉你这并不是一个典型的应用,虽然 XAML loader 会在和其它面向应用的对象模型完全隔离的情况下用 XAML 来建立一个。
另一个秘密:虽然没有写出语法(会说这是不适用的东西),但是在 frameworks 中有很多支持 XAML 的类。一般来说,这是因为这些类来自更早的版 本,可以在整个 3.0 framework 中使用,因为可能合法的 XAML 使用率会很大。所以,为了填补空白,用户需要知道一些 XAML 的技巧。对初学者,读 XAML and Custom Classes 就可以了。当以默认的 ctor 和一些其它的限定来符合 XAML 加载器的要求时,定制的类和已有的类没什么不同的。也去看看 x:Array Markup Extension 中的例子吧。这个例子讲了系统名字空间和 mscorlib 组件,这样你就可以吧 String 类作为一个直接对象进行实例化。你可以外推 这些简单的概念,并做些更有用的事情。甚至设想这个场景都有点困难,但是你们是一个充满创造性的集体:让我们看看你们能做什么!描绘一些 System.Data 的结构?谁知道在 pre-3.0 .NET 的核心部分用了多少 XAML ?
没有 XAML 属性列表?
SDK 并不是真的有一个 “ 所有权页面 ” ,这是 XAML 特有的。你可以把 property 列表看作是普通的成员表的一部分,但要选出 XAML 属性有点困难, 因为有些是只读的,有些又不使用支持 XAML 的类型,等等。这里,我想我们不得不承认工具和设计者可以比只利用 SDK 的成员表做得更好。因为工具和设计者 有语义的优势;他们知道你刚刚实例化了什么面板子类,在元素树里面它处于什么位置等。好好地综合基于工具的只列出基本的可能性的智能,然后学会按 F1 来获 取 SDK 页面的更多的帮助信息。这是我们追赶即将来临的工具 /SDK 的发行的下一步。限于篇幅,不能细细讲解。
没有 XAML XSD 的计划?
我以前在博客上写过这个。对一个像 WPF 这样有很多维的产品来说, XAML 的计划很困难。但你也许已经注意到接下来的 XAML 综合技术( Workflow Foundation, Silverlight )确实包括了 XSD 类型的计划。说到 WPF XAML 的计划,我们仍然保持观望。
看了一遍,感觉太浮云了