在加载XAML(或BAML)和接收根对象(例如Window)之间内部会发生什么?
首先出现在我脑海中的是Reflecton用于创建对象,设置其属性等等。但也许我错了?
也许有人可以解释如何在运行时解析和执行XAML / BAML,或者提供一篇包含解释的好文章的链接?
为了使我的问题更清楚一点,让我们讨论一下这个简短的例子:
<Button Margin="10">OK</Button>
因此,解析器看到需要创建一个Button对象,其Margin属性必须设置为10并且其内容必须设置为“OK”。怎么做的?通过使用Reflection(加上TypeConverters等)?
答案 0 :(得分:27)
您可能会在.xaml.cs
文件中注意到支持已编译的XAML文件的类被标记为partial
类。 XAML构建任务生成第二个.cs
文件,其中另一个部分类部分包含IComponentConnector.InitializeComponent()
方法的实现,该方法由后面的代码中的默认构造函数调用。这个方法基本上贯穿XAML(此时实际上是BAML形式)并使用它来修复&#34;新创建的对象,而不是从XAML源创建新对象,如果您使用XamlReader
加载或解析对象,将会发生这种情况。
因此,当您实例化一个新编译的XAML对象(例如,UserControl
)时,在构造函数中调用InitializeComponent()
之前的任何代码都将执行。然后,在调用InitializeComponent()
期间将处理在XAML文件中设置的所有属性和事件处理程序,之后构造函数将继续。这可能很有用,因为您可能希望确保在处理XAML文件之前或之后设置某些属性。
关于如何解析XAML,它基本上被读作表示属性赋值,对象声明等的XAML节点流,它们由System.Xaml
中的服务按顺序执行。此节点流基于公共对象模型,该模型可能是从BAML流,XML文档(例如,松散的.xaml
文件),另一个对象实例等构建的.BAML比其更紧凑。基于XML的格式通常更快解析。
附录:在您添加的示例中,您询问解析器如何看到需要创建Button
对象并设置Margin
。简短的回答是:这取决于。具体来说,它取决于用于读取XAML流的模式上下文。
XAML解析器使用自己的类型系统,其中至少有两个实现:
System.ComponentModel
; 根据我对XAML语言规范的回忆,大致会发生什么:
StartObject
类型的Button
节点,该节点(对于标准WPF命名空间映射)解析为System.Windows.Controls.Button
。这告诉解析器它需要创建一个Button
对象的实例,它通过反射调用它的默认构造函数。StartMember
节点,成员名称为Margin
。 WPF模式上下文的类型模型将此解析为Margin
依赖项属性。Value
节点,它告诉解析器设置值"10"
(字符串)。解析器发现属性类型Thickness
与字符串值不兼容。它查询其类型系统以查看[ValueSerializer]
属性上是否存在Margin
属性,该属性可用于转换字符串;没有这样的属性。它会检查属性是否为[TypeConverter]
属性;再一次,它找不到。它在[TypeConverter]
类型本身上查找Thickness
属性并找到一个,指示它使用ThicknessConverter
将字符串值转换为Thickness
。它就是这样。由于Margin
是依赖项属性,因此它使用SetValue()
API来设置属性值;如果它是CLR属性,它将使用反射或PropertyDescriptor
。EndMember
节点告诉解析器结束属性赋值。Value
的{{1}}节点。解析器知道它正在构造一个复杂的对象,因此内容不能代表整个对象。它在"OK"
及其超类型上查找[ContentProperty]
属性;它在Button
上找到一个,表示该值应该用于设置ContentControl
属性(它被解析为相应的依赖属性)。 Content
是Content
,因此它会直接指定object
值(再次使用string
)。SetValue()
,它告诉解析器它已经完成了对EndObject
对象的处理。请注意,我使用了术语&#34;解析器&#34;简化事情。说实话,这一切都不会发生在解析阶段(如果&#34;解析&#34;阶段甚至存在)。您可以将其视为&#34;解析&#34; stage只是构建一个XAML节点流。声明对象的创建和/或填充实际上是通过将该流馈送到Button
来实现的,这只是XamlObjectWriter
的实现,它将XAML节点写入对象(而不是XML文档或BAML流)。在高层,只发生了两件事:
XamlWriter
将某些内容转换为XAML节点流。XamlReader
将XAML节点流转换为某种形式。对于编译的XAML资源,编译时构建任务将XamlWriter
的输出传输到XamlXmlReader
到&#34;编译&#34; XAML。在运行时,BamlWriter
的输入通过管道传输到BamlReader
以创建或修复&#34;根对象。
一旦理解了所有这些,您就可以开始认识到XAML是一种强大的序列化和持久化格式,而不仅仅是用于构建UI的语言。