Silverlight API 将它的一组对象定义为对象树,这样您可以通过加载 XAML 来填充基于 Silverlight 的应用程序的初始内容,然后在运行时调整对象树。在托管 API 中,与 Silverlight 对象树的交互是通过托管代码定义的。使用 CLR 可将托管代码编译为程序集。该程序集通常还包含用于基于 Silverlight 应用程序的 XAML。托管代码可支持 Silverlight 应用程序模型,可以调用托管代码以响应对象生存期事件或用户启动事件。还可以使用托管代码编写支持类,定义数据对象以及用于许多其他应用程序方案。您还可以使用 DLR 而不是 CLR,Silverlight 中的动态语言 对此进行了讨论。
托管 API 的特性
托管代码是可扩展的
在以前的 Silverlight 1.0 中和当前的 JavaScript API(与客户端版本无关)中,不支持定义真正的自定义 UI 元素。您可以合成一个元素,然后从同一定义 XAML 多次加载它,并将相同的函数指定为代码支持,但这种方法不支持大型扩展。使用托管 API,扩展实质上只是从支持有用基本功能的现有类或控件派生,可能还包括定义某些支持元素,例如新控件模板、更新的类特定的事件处理程序等。扩展托管代码是托管编程和 .NET Framework 编程的基本概念,在 Silverlight 文档中没有专门的主题,但下列主题可能非常有用:
-
公共语言运行时
-
控件自定义
-
XAML 和自定义类
-
自定义依赖项对象和依赖项属性
对象树
对象树概念存在于 JavaScript API 中,但在该模型中操作对象树在某种程度上受对象构造方法缺乏的限制。在托管 API 中,可以对类调用构造函数,无需使用 XAML 分析器作为中间源即可向树添加对象。实际上,仅使用代码即可构建树的大部分,方法是:连续调用构造函数,从根开始向对象树添加对象,然后完成各种"子"属性。有关如何通过代码使用 Silverlight 对象树的更多信息,请参见 Silverlight 对象树。
托管代码隐藏和分部类
在 Silverlight 中定义 UI 的主要方法是在 XAML 标记中定义相应的元素。这可能包括使用样式和模板,它们也是在 XAML 标记中定义的。很多情况下,需要将标记分解为与 Silverlight 应用程序模型集成的不同 XAML 文件。为了定义应用程序所需的交互,通常需要为 XAML 中定义的对象定义事件处理程序,以响应对象生存期事件或用户启动事件。在托管 API 中,需要事件处理的每个 XAML 页或其中的 XAML 要作为根元素内容的每个 XAML 页必须作为生成的一部分进行标记编译处理。标记编译处理提供了必要的挂接,以便与加载 XAML 页时创建对象树进行运行时交互。这些挂接包括在 XAML 中赋予其 Name 或 x:Name 的任何对象的事件处理程序和引用。在 Silverlight 体系结构中,XAML 不进行完整编译。在运行时将 XAML 页真正加载到应用程序中并由 Silverlight XAML 分析器处理之前,会推迟创建来自 UI 的 XAML 标记的大部分对象树。代码隐藏和分部类中对这些概念进行了更加详细的说明。
规范化 DOM 交互
在 JavaScript API 中,可以非正式地从 Silverlight 脚本引用 HTML DOM 中的对象或变量,反之亦然,这是因为脚本由同一浏览器脚本引擎解释并可能位于同一个作用域中。在托管 API 中存在更正式的分离,因为托管代码由 Silverlight 核心库及其 CLR 使用,而不是由浏览器使用。为了从托管 API 与 DOM 进行交互,可以使用一系列功能,这些功能有时统称为 HTML Bridge,或 HTML DOM Bridge。这些功能包括:
-
将 DOM 的常用部分(如 Document 对象
- 使托管类型可从 DOM 编写的功能。
- 用于从封送 JavaScript 值或对象生成托管类型的转换技术。
- 专门用来在 DOM 中公开 Silverlight 插件的对象表示形式的 API。
在托管代码或 JavaScript 中,您可以在运行时期间引用 Silverlight 的 Silverlight 对象树中的对象。本主题介绍如何在 Silverlight 托管 API 中使用对象树。
本主题包括下列各节。
- 先决条件
- 对象树
- 对象树和 XAML 标记
- 引用对象属性
- 对象树中的资源和模板
- 遍历对象树
- 使用 VisualTreeHelper
- 遍历模板内容
- Silverlight 对象和 HTML DOM
- JavaScript API
- 相关主题
对象树
对象树概念描述如何使在运行时在 Silverlight 内容中创建和存在的对象彼此相关。关系基于对象具有属性这一原则,在很多情况下属性的值是另一个对象,而此对象也具有属性。对象树具有分支,因为其中某些属性是集合属性并具有多个对象;并且,对象树具有根,因为体系结构最终必须引用单个对象,而该对象是与对象树之外的概念(例如,浏览器宿主或显示内容的 Silverlight 插件)之间的连接点。
尽管在概念上实际只有一个对象树,但 Silverlight API 不向您公开完整的树。大量的对象树结构实际上是实现详细信息。而您具有对象特定的属性,这些属性影响树中特定点的子项值并且可能报告父项(在大多数情况下,父轴是只读的,因为您通常是在代码中或通过 XAML 分析过程从根向上构建树)。例如,Panel 具有其 Children 属性,该属性设置子对象。FrameworkElement 具有用于报告父项的 Parent。这两个 API 都在基类中,因此,它们可用于大量的 Silverlight 对象。
Silverlight 中一个相关的树概念是可视化树。可视化树概念指的是较大的对象树在经过编辑或筛选后的表示形式。所应用的筛选器是在可视化树中只存在具有呈现含义的对象。例如,某个集合类不是可视化树的一部分,而可视化树将集合抽象为一个"子项"概念。然而,如果您将加载的源 XAML 标记视为与对象树近似的结构,则可视化树也可以包含并不直接显示的对象。这是因为,可视化树也报告作为特定控件(这些控件来自所应用的控件模板或资源字典)的组成部分的对象。可视化树在内部用于 Silverlight 呈现过程,但了解一些有关可视化树的内容对于某些情形通常很重要,例如,在应用模板后编写或替换控件模板或在运行时分析控件实例。对于这些情形,Silverlight 提供了 VisualTreeHelper API,它通过一种方式检查可视化树,这种方式比您通过对象特定的父属性和子属性来实际实现更为一般化。
可视化树概念也存在于 WPF 中,它与 Silverlight 的可视化树概念类似。然而,一个显著的差异是 WPF 还提供一个附加的筛选器或对象树(称为"逻辑树")的概念。逻辑树概念与某些属性系统行为相关。Silverlight 不通过帮助器类来公开此逻辑树。Silverlight 中的确存在某些(但并非所有)相关的属性行为,但由于没有用于访问这些行为的帮助器 API,因此,逻辑树概念在 Silverlight 中将没有用武之地,因此本文档不讨论它。缺少逻辑树而引发的一个很小的兼容性问题是:FrameworkElement..::..Parent 属性行为在 Silverlight 版本 3 中是不同的,它实际上报告可视化树父项。
对象树和 XAML 标记
如果您将通过 Silverlight API 访问的对象树与 XAML 标记的树形状进行比较,它们在节点方面并不完全匹配。这是因为 XAML 用于标记,并且在标记定义期间易于使用。例如,XAML 具有属性元素的概念,它提供相关的指导信息,当您发现一个元素嵌套在另一个元素内时,您可以选择要设置的属性。在对象树中,这就像一个对象上的某个属性由另一个对象进行设置一样。相反,XAML 也具有内容属性的概念,其中,所设置的属性在标记中甚至没有显式进行命名。XAML 具有的语法可以基于属性的字符串值创建对象,也可以针对 XAML 标记中已存在但在其他位置定义的对象提供引用,或者完全位于标记外部。尽管存在这些很小的不一致,但当您在 XAML 中定义用户界面时,您将在运行时定义最终 Silverlight 对象树的近似结构。
有关 XAML 的特定术语和规则的更多信息,请参见 XAML 概述。
引用对象属性
无论您通过何种方式从 Silverlight 对象树中获取对象引用,都将通过 object.property 表示法(与 .NET 中 CLR 属性的核心概念相关)直接公开托管 API 中的属性。许多 Silverlight 2 属性的基础是依赖项属性概念。依赖项属性和属性系统引入了一些其他可能的语法,用于通过与 object.property 不同的方式访问属性,但与附加属性的情况不同,这些属性并不常用,因此本主题不讨论它们。主题依赖项属性概述中详细讨论了依赖项属性。
对象树中的附加属性
Silverlight 支持附加属性的概念。从对象树的角度来看,附加属性是可以附加到树中任何对象的属性,而不考虑该对象的类型(尽管在 Silverlight 实现中,该对象至少必须是依赖项对象)。附加属性值存在于对象树中,但是,如果您使用代码来访问它们,则必须使用与 object.property 表示法不同的语法。有关详细信息,请参见附加属性概述。
对象树中的资源和模板
Silverlight 支持一个称为资源字典的资源概念。资源字典用于指定自身需要大量子属性设置的属性值。
ResourceDictionary 最常见的方案是在 XAML 中定义 ResourceDictionary 元素,然后通过 XAML 属性 (attribute) 和 StaticResource 标记扩展将已定义的资源用作属性 (property) 值。对于某些情况,可以共享此资源。例如,您可以定义一个 LinearGradientBrush(它在 ResourceDictionary 中包含多个渐变停止点),然后将其应用于可视化设计中的多个 Brush 属性(可能在位于不同页的用户界面中)。
模板将按稍微不同的概念运行(无论是在页级别还是在应用程序级别资源字典中定义,也无论是在 generic.xaml 中还是以内联方式)。模板自身是一个对象,但模板可能多次应用于可视化树。在应用后,模板中的元素通常使用 TemplateBinding,这样,就可以应用模板并仍然设置由模板化对象保留的特定值。有关模板的基础概念将在主题通过使用 ControlTemplate 自定义现有控件的外观中讨论。
遍历对象树
遍历对象树在对象模型中是一种通用的方法。遍历树意味着您可以使用针对包含对象引用子对象(通常,这些是集合)或父关系的属性(这通常是在集合内完成的,并返回集合自身)。我们可以对此过程进行粗略的说明:您调用一连串子属性和父属性(或可能调用帮助器方法)以导航对象树的各个轴,直到您检索到包含您所查找的对象的值。
通常,您应该可以在 XAML 中针对 Silverlight 构造您的内容,这样,您就不需要大量查询树的结构。为了避免需要遍历树,请在创建元素的 XAML 中对于 x:Name / Name 属性向这些元素提供一个值。这就创建了一个直接引用,该引用可用于标记从 XAML 编译的类中,与遍历树相比,这种获取对象的方法出错的可能性要低得多。
此外,如果您通过代码构造函数而不通过 XAML 加载来创建对象,则您应该能够构造您的代码,以便您可以定义私有字段或变量来在运行时保留对象引用(保留在类中,或在应用程序级别存储为变量)。
然而,在某些情况下,向对象提供名称并在范围中保留对象引用是不可能的,也是不切实际的。一个此类方案是:您正在添加由用户提供或通过数据绑定提供的动态内容,而您无法预测所添加的项数或运行时对象树的结构。另一个方案是检查对于某个控件所应用的模板,或控件的某个组成部分。
| 警告: |
|---|
|
Silverlight 通常支持"设置外观"概念,也称为重新设置控件样式或控件重新模板化。尤其是,如果您是控件作者且正在编写控件的支持代码,则假定特定的树结构可能很危险。因为大多数控件支持可设置的模板(无论您是否已启用多个特定的扩展点,如子部分样式),所以,运行时可视化树可能与通过所应用的默认模板创建的树不同。请参见通过使用 ControlTemplate 自定义现有控件的外观。 |
用于遍历"子项"和其他集合的 Try-catch 逻辑
如果您遍历对象树的要求涉及到查找某些对象,而这些对象所表示的集合未表示为可视化树的一部分,则您可能需要编写专用的函数,以便尝试查找与特定的命名模式或特定类的对象模型相匹配的 API。
向下(远离根)遍历对象树的多个级别通常是可能的,只要您了解所包含的对象将具有集合的点。您可能必须使用 try/catch 方法或其他等效方法来检测这一点,即检查 Children 是否存在以及 Count 是否为非零值(此处的 Children 和 Count 是占位符,而不是文字 API;根据 .NET 命名原则,Children 和 Count 刚好是这些类型的属性的公共名称,但根据对象及其对象模型,实际属性可能具有不同的名称)。总体 Silverlight 对象模型中的某些集合包含在未命名为 Children 的属性中。如果您知道您正在遍历到某个未命名为 Children 的特定集合属性,则应在逻辑中说明此情况。
使用 VisualTreeHelper
VisualTreeHelper 是一个可用于遍历对象树的实用工具类。(可视化树的概念已在本主题前面的"对象树"一节中介绍。)
因为您可以在运行时对可视化树执行操作,并且可以遍历到模板部件,所以这可能是一种可用来检查模板组成情况的有用手段。此外,您可以检查可能通过数据绑定填充的子集合,或者您的应用程序代码可能无法全部了解运行时对象树的完整本质的子集合。为此,您可以通过 GetChild 并将 GetChildrenCount 用作一个确定因素(确定树节点是单个项还是应按计数进行迭代的"子项"集合)来遍历该树。
遍历模板内容
除 VisualTreeHelper 之外,可用来遍历模板内容的另一个方法是 GetTemplateChild。使用 GetTemplateChild 或遍历模板内容通常是必要的,因为 FindName 的行为由名称范围概念控制。在这种情况下,模板内容与对象树的其他部分具有特意不同的名称范围,因为模板是共享的,如果不采用单独的名称范围,则在多次应用模板时会导致名称冲突。GetTemplateChild 按其模板名称范围 x:Name 值查找对象,同时从应用该模板的特定 Control 的更大对象树范围中遍历。
有关名称范围概念的更多信息,请参见 XAML 名称范围。
Silverlight 对象和 HTML DOM
还有另一个对象模型可用于为 HTML 编写脚本:HTML 文档对象模型 (DOM)。然而,DOM 不会将 Silverlight 插件加载的内容标识为 DOM 的一个完整部分。有关 DOM 以及 DOM 与 Silverlight 对象之间差异的更多信息。
浙公网安备 33010602011771号