您可能会发现在您的
.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
文件)、另一个对象实例等构造。相对于基于 XML 的格式,BAML 更紧凑,通常解析速度更快。
补充说明:在您添加的示例中,您问解析器如何看到需要创建一个
Button
对象并设置
Margin
。简短的答案是:这取决于用于读取 XAML 流的模式上下文。
XAML 解析器使用自己的类型系统,其中至少有两个实现:
基于反射和System.ComponentModel
的标准CLR类型系统;
A WPF类型系统扩展#1,包括对依赖属性和路由事件的特殊支持。
大致上,在我的XAML语言规范记忆中,以下是发生的情况:
- XAML解析器遇到
Button
类型的StartObject
节点,(对于标准WPF名称空间映射),它将解析为System.Windows.Controls.Button
。这告诉解析器需要创建一个Button
对象的实例,通过反射调用其默认构造函数来完成此操作。
- 接下来的节点是
StartMember
节点,成员名为Margin
。WPF模式上下文的类型模型将把它解析为Margin
依赖属性。
- 接着是一个
Value
节点,它告诉解析器要设置一个值"10"
(一个字符串)。解析器发现属性类型Thickness
与字符串值不兼容。它查询其类型系统,看看Margin
属性上是否存在[ValueSerializer]
属性,以转换字符串;没有这样的属性。它检查Margin
属性是否有[TypeConverter]
属性;同样,它找不到。它在Thickness
类型本身上查找[TypeConverter]
属性,并找到一个,它指示解析器使用ThicknessConverter
将字符串值转换为Thickness
。然后它这样做了。由于Margin
是依赖属性,因此它使用SetValue()
API来设置属性值;如果它是CLR属性,则会使用反射或PropertyDescriptor
。
EndMember
节点告诉解析器结束属性赋值。
解析器遇到一个
Value
节点,其内容为
"OK"
。解析器知道它正在构建一个复杂对象,因此该内容无法代表整个对象。然后解析器会在
Button
及其超类型上查找
[ContentProperty]
属性;它在
ContentControl
上找到了该属性,这表明该值应用于设置
Content
属性(该属性将被解析为对应的依赖属性)。由于
Content
是一个
object
,所以它直接分配了该
string
值(再次使用
SetValue()
)。
下一个节点是
EndObject
,告诉解析器已经完成处理
Button
对象。
请注意,我使用术语“解析器”来简化事情。实际上,这些都不是在解析阶段发生的(如果“解析”阶段甚至存在的话)。您可能认为的“解析”阶段实际上只是构造XAML节点流。声明对象的创建和/或填充实际上是通过将该流馈送到
XamlObjectWriter
中进行的,它只是将XAML节点写入对象的
XamlWriter
的实现(而不是XML文档或BAML流)。从高层次来看,只有两件事情在发生:
XamlReader
将某些东西转换为 XAML 节点流。
XamlWriter
将 XAML 节点流转换为某些东西。
对于复杂的 XAML 资源,编译时构建任务将
XamlXmlReader
的输出导入到
BamlWriter
中以“编译”XAML。运行时,
BamlReader
的输入将被导入到
XamlObjectWriter
中以创建或“修正”根对象。
一旦你理解了所有这些,你可能会开始认识到XAML不仅仅是用于构建UI的语言,而是一个强大的序列化和持久化格式。