对象树和 FindName
在 JavaScript API 中,初始对象树始终通过分析 XAML 来构造。除了一些能够分析字符串的类型转换用
法,加载 XAML 是在 JavaScript API 中“构造”对象的唯一方式。构造了这样的树之后,您通常会希望与该
对象树进行交互。为此,需要脚本对象引用树中的一个对象。
建立这样的引用的最佳方法是将 Name 或 x:Name 属性分配给计划在运行期间引用对象所在的 XAML 中的
任何对象元素。在 JavaScript API 中,名称在概念上只是一个字符串,而非对象。但可以随后通过在任何运
行时代码中调用 FindName,使用名称字符串检索实际对象。FindName 以这种方式存在于 API 中:您可以在
JavaScript API 中的几乎所有对象(包括表示 Silverlight 插件的对象)上调用 FindName。您通常会发现
为用于 Silverlight 的 JavaScript API 编写的用户函数的第一行或前几行使 FindName 调用获取对象引用
,然后函数的其余部分对那些对象进行操作。
即使没有命名对象,您仍然可以在运行时通过 Silverlight 插件上的 Root 属性进入对象树。这将返回作
为加载的 XAML 的根的对象。
此外,任何附加到 Silverlight 对象的事件处理程序都有权从事件访问 sender 值。该 sender 和相关事
件处理程序通常以这样的方式进行附加:该 sender 是您希望与其交互的对象。如果不是这样,sender 仍提
供可以从中调用 FindName 的方便对象。
对象树和 CreateFromXaml
网页上的 Silverlight 内容按树状结构中的对象层次结构进行排列。结构中最顶层的那一个对象是根对象
,并且对应于作为 Silverlight 插件的 Source 加载的 XAML 文件的根。根对象通常是一个 Canvas 对象,
因为 Canvas 可以包含其他对象,如 TextBlock 或 MediaElement。(Silverlight 1.0 仅支持 Canvas 作为
根;如果将 JavaScript API 用于 Silverlight 2 客户端,则可以使用其他 UIElement 对象作为根。)
若要在初始加载 source XAML 后将 XAML 内容动态添加到树状结构中,请先使用 CreateFromXaml 方法创
建 XAML 片段。此时,创建的 XAML 片段与 Silverlight 对象树是断开连接的。这意味着尚未呈现该片段。
不过,即使在将其添加到对象树之前,仍可以修改该 XAML 片段中对象的属性。不能在连接到树之前调用对象
方法。
在创建与 XAML 片段断开连接的对象集后,您通常会希望将该对象集添加到活动的 Silverlight 对象树中
。可以将该片段作为子对象添加到某个父对象中,也可以将该片段设置为某个对象的属性值。当该片段成为
Silverlight 对象树的一部分后,将呈现该 XAML 片段中的对象。根据作为 CreateFromXaml 输入提供的
XAML 范围,可以创建一个 Silverlight 对象(如 TextBlock)或复杂的 Silverlight 对象树。
对于将 XAML 内容动态添加到 Silverlight 对象树有以下几个要求:
必须有与 Silverlight 插件关联的现有 XAML 内容。不能使用 CreateFromXaml 替换整个内容树;必须
至少保留原始根元素。如果希望替换整个树,则可以设置 Source,但请注意,Source 需要 URI 处存在实际
的文件,并且不能仅用字符串来进行初始化。但是,可以通过下面的方法来解决此问题:使用内联 XAML 作为
Source,使用 HTML DOM 来 document.write 包含预期源 XAML 的 SCRIPT 块的内容。
CreateFromXaml 方法只能由 Silverlight 插件调用。不过,在 JavaScript API 中很容易获取对该插件
的对象引用:只需在任何 Silverlight 对象上调用 GetHost。
只能将使用 CreateFromXaml 方法创建的 XAML 内容分配给一个对象。如果希望将从同一 XAML 创建的对
象添加到应用程序的不同区域,则必须多次调用 CreateFromXaml,并对返回值使用不同变量。(如果执行此
操作,请小心名称冲突;请参见本主题后面的使用 CreateFromXAML 创建名称范围。)
将要包含新的 XAML 内容的 Silverlight 对象必须能够封装对象,无论是作为子元素还是作为属性值。
为 CreateFromXaml 的 xamlContent 指定的字符串必须指定格式标准的 XML(具体地说,它必须具有单个
根元素)。如果提供的 XML 字符串的格式不正确,CreateFromXaml 将失败并导致错误。
根元素隐式使用 Silverlight XML 命名空间。可以显式设置 Silverlight 命名空间,但不要将默认 XML
命名空间设置为 Silverlight 命名空间以外的任何其他值。
使用 CreateFromXAML 创建名称范围
用于 CreateFromXaml 的 XAML 可以具有为 Name 或 x:Name 定义的值。在 CreateFromXaml 调用过程中
,将基于所提供的 XAML 的根创建初步的名称范围。该初步名称范围计算所提供的 XAML 中的所有已定义名称
是否唯一。如果所提供的 XAML 中的名称此时在内部不唯一,CreateFromXaml 将引发错误。但是,如果所提
供的 XAML 中的名称与主对象树名称范围中已经存在的名称冲突,则不会立即发生任何错误。这是因为调用
CreateFromXaml 方法时,返回的所创建的对象树是断开连接的。必须通过以下方法显式连接所创建的对象树
:将它添加到内容属性集合(如 Canvas.Children)中,或者设置另外一个采用对象值的属性(例如,为
Fill 属性值指定一个新的 ImageBrush)。只有在尝试将断开连接的对象树连接到应用程序的主对象树之后,
才能看到导致错误的名称冲突。用于所尝试的连接的方法或属性将是错误的源,并且所尝试的连接将失败(断
开连接的树将保持断开连接状态)。
CreateFromXaml 具有一个可选参数 createNameScope,该参数通常默认为 false。如果在用于创建断开连
接的对象树的 CreateFromXaml 调用中将该值显式设置为 true,仍然会创建初步的名称范围来计算所提供的
XAML 中的所有已定义名称是否唯一。如果所提供的 XAML 中的名称此时在内部不唯一,CreateFromXaml 将引
发错误。行为上的差异为,断开连接的对象树将随即被标记为,当它与主应用程序对象树连接后,将不尝试将
其名称范围与主应用程序名称范围合并在一起。实际上,在连接树之后,应用程序将具有一个统一的对象树,
而不是具有独立的名称范围。在 CreateFromXaml 根或以前断开连接的树中的任何对象上定义的名称都不与主
对象树名称范围关联;它具有自己的独立名称范围。
具有独立名称范围会带来以下麻烦:对 FindName 方法的调用将不再对统一的名称范围执行操作。相反,
调用 FindName 的特定对象将表示一个范围,而该范围就是调用对象所在的名称范围。因此,如果尝试调用
FindName 来获取主对象树名称范围中的命名对象,将找不到由 CreateFromXaml 创建的独立名称范围中的对
象。相反,从独立名称范围调用 FindName 将找不到主应用程序名称范围中的命名对象。对 Silverlight 插
件对象定义的 FindName 方法不会完全解决此问题;其名称范围始终为主对象树名称范围。
此独立名称范围问题仅影响名称范围和 FindName 调用。在某些情况下仍然可以通过调用 GetParent 方法
向上执行分离的步骤,或者通过调用相关集合属性或属性(如 Canvas.Children 返回的集合)向下执行分离
的步骤。
若要获取在不同名称范围中定义的对象,则可以使用以下多种技术:
使用 GetParent 和/或集合属性在分离的步骤中执行整个树。
如果从独立名称范围进行调用并希望得到主对象树名称范围,则始终最简单的做法是获取对 Silverlight
插件的引用,然后对其调用 FindName。若要在单行中执行此操作,则连接的脚本调用如下所示:
returnedObject = 对象.GetHost().content.FindName("nameToFind");
其中,对象 是独立名称范围中的调用对象。此语法中的 .content 是通往定义 FindName 方法的特定
Silverlight 插件子对象的步骤。
如果从主对象树名称范围进行调用并希望得到独立名称范围内的对象,则最佳做法是在代码中提前进行计
划并保留对由 CreateFromXaml 返回的对象的引用,然后添加到树中。现在,此对象对于在所创建的独立名称
范围内调用 FindName 是有效的对象。可以将此对象保持为全局变量,否则使用方法参数传递它。