您可能会注意到,在您的 .xaml.cs 文件中,支持已编译 XAML 文件的类被标记为 partial 类。 XAML 构建任务生成第二个.cs 文件,其中包含另一个包含IComponentConnector.InitializeComponent() 方法实现的部分类部分,该方法由后面代码中的默认构造函数调用。此方法基本上贯穿 XAML(此时实际上是 BAML 形式)并使用它来“修复”新创建的对象,而不是从 XAML 源创建新对象,如果你是使用XamlReader 来加载或解析对象。
因此,当您实例化一个新的已编译 XAML 对象(例如,UserControl)时,构造函数中调用 InitializeComponent() 之前的任何代码都将执行。然后,在调用InitializeComponent() 期间将处理在 XAML 文件中设置的所有属性和事件处理程序,之后构造函数将恢复。了解这一点很有用,因为您可能希望确保在处理 XAML 文件之前或之后设置某些属性。
关于 XAML 的解析方式,它本质上被读取为表示属性分配、对象声明等的 XAML 节点流,这些由System.Xaml 中的服务按顺序执行。这个节点流基于一个通用对象模型,它可能是由一个 BAML 流、一个 XML 文档(例如,一个松散的.xaml 文件)、另一个对象实例等构成的。BAML 比 XML-基于格式,通常解析速度更快。
附录:在您添加的示例中,您询问解析器如何看到需要创建Button 对象和Margin 设置。简短的回答是:这取决于。具体来说,它取决于用于读取 XAML 流的架构上下文。
XAML 解析器使用自己的类型系统,其中至少有两种实现:
- 基于反射和
System.ComponentModel的标准CLR类型系统;
- 一种 WPF 类型系统,通过包括对依赖属性和路由事件的特殊支持来扩展 #1。
这是,根据我对 XAML 语言规范的回忆,大概会发生什么:
- XAML 解析器遇到
Button 类型的 StartObject 节点,该类型(对于标准 WPF 命名空间映射)解析为 System.Windows.Controls.Button。这告诉解析器它需要创建一个 Button 对象的实例,它通过反射调用其默认构造函数来实现。
- 流中的下一个节点是
StartMember 节点,成员名称为Margin。 WPF 架构上下文的类型模型会将其解析为 Margin 依赖属性。
- 接下来是一个
Value 节点,它告诉解析器设置一个值"10"(一个字符串)。解析器看到属性类型Thickness 与字符串值不兼容。它查询其类型系统以查看Margin 属性上是否存在[ValueSerializer] 属性,该属性可用于转换字符串;不存在这样的属性。它检查[TypeConverter] 属性的属性;再次,它没有找到。它在Thickness 类型本身上查找[TypeConverter] 属性并找到一个,该属性指示它使用ThicknessConverter 将字符串值转换为Thickness。它这样做了。由于Margin是一个依赖属性,所以它使用SetValue() API来设置属性值;如果它是 CLR 属性,它将使用反射或 PropertyDescriptor。
-
EndMember 节点告诉解析器结束属性分配。
- 解析器遇到内容为
"OK" 的Value 节点。解析器知道它正在构造一个复杂的对象,因此内容不能代表整个对象。它在Button 及其超类型上查找[ContentProperty] 属性;它在ContentControl 上找到一个,这表明该值应该用于设置Content 属性(它被解析为相应的依赖属性)。 Content 是 object,因此它直接分配 string 值(同样,使用 SetValue())。
- 下一个节点是
EndObject,它告诉解析器它已经完成了对Button对象的处理。
请注意,我使用“解析器”一词来简化事情。说实话,这一切都不会发生在解析阶段(如果“解析”阶段甚至存在的话)。您可能认为的“解析”阶段只是构建 XAML 节点流。声明对象的创建和/或填充实际上是通过将该流输入XamlObjectWriter 来实现的,这只是XamlWriter 的实现,它将XAML 节点写入对象(而不是XML 文档或BAML 流)。在高层次上,只有两件事发生:
-
XamlReader 将某些内容转换为 XAML 节点流。
-
XamlWriter 将 XAML 节点流转换成某种东西。
对于已编译的 XAML 资源,编译时构建任务将 XamlXmlReader 的输出通过管道传输到 BamlWriter 以“编译” XAML。在运行时,BamlReader 的输入通过管道传输到 XamlObjectWriter 以创建或“修复”根对象。
一旦您了解了所有这些,您可能会开始认识到 XAML 是一种强大的序列化和持久性格式,而不仅仅是一种用于构建 UI 的语言。