XAML如何在运行时解释和执行?

时间:2013-12-11 20:07:15

标签: .net wpf xaml

在加载XAML(或BAML)和接收根对象(例如Window)之间内部会发生什么?

首先出现在我脑海中的是Reflecton用于创建对象,设置其属性等等。但也许我错了?

也许有人可以解释如何在运行时解析和执行XAML / BAML,或者提供一篇包含解释的好文章的链接?

为了使我的问题更清楚一点,让我们讨论一下这个简短的例子:

<Button Margin="10">OK</Button>

因此,解析器看到需要创建一个Button对象,其Margin属性必须设置为10并且其内容必须设置为“OK”。怎么做的?通过使用Reflection(加上TypeConverters等)?

1 个答案:

答案 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解析器使用自己的类型系统,其中至少有两个实现:

  1. 基于反射和System.ComponentModel;
  2. 的标准CLR类型系统
  3. WPF类型系统,通过包含对依赖项属性和路由事件的特殊支持来扩展#1。
  4. 根据我对XAML语言规范的回忆,大致会发生什么:

    1. XAML解析器遇到StartObject类型的Button节点,该节点(对于标准WPF命名空间映射)解析为System.Windows.Controls.Button。这告诉解析器它需要创建一个Button对象的实例,它通过反射调用它的默认构造函数。
    2. 流中的下一个节点是StartMember节点,成员名称为Margin。 WPF模式上下文的类型模型将此解析为Margin依赖项属性。
    3. 接下来是Value节点,它告诉解析器设置值"10"(字符串)。解析器发现属性类型Thickness与字符串值不兼容。它查询其类型系统以查看[ValueSerializer]属性上是否存在Margin属性,该属性可用于转换字符串;没有这样的属性。它会检查属性是否为[TypeConverter]属性;再一次,它找不到。它在[TypeConverter]类型本身上查找Thickness属性并找到一个,指示它使用ThicknessConverter将字符串值转换为Thickness。它就是这样。由于Margin是依赖项属性,因此它使用SetValue() API来设置属性值;如果它是CLR属性,它将使用反射或PropertyDescriptor
    4. EndMember节点告诉解析器结束属性赋值。
    5. 解析器遇到内容为Value的{​​{1}}节点。解析器知道它正在构造一个复杂的对象,因此内容不能代表整个对象。它在"OK"及其超类型上查找[ContentProperty]属性;它在Button上找到一个,表示该值应该用于设置ContentControl属性(它被解析为相应的依赖属性)。 ContentContent,因此它会直接指定object值(再次使用string)。
    6. 下一个节点是SetValue(),它告诉解析器它已经完成了对EndObject对象的处理。
    7. 请注意,我使用了术语&#34;解析器&#34;简化事情。说实话,这一切都不会发生在解析阶段(如果&#34;解析&#34;阶段甚至存在)。您可以将其视为&#34;解析&#34; stage只是构建一个XAML节点流。声明对象的创建和/或填充实际上是通过将该流馈送到Button来实现的,这只是XamlObjectWriter的实现,它将XAML节点写入对象(而不是XML文档或BAML流)。在高层,只发生了两件事:

      1. XamlWriter将某些内容转换为XAML节点流。
      2. XamlReader将XAML节点流转换为某种形式。
      3. 对于编译的XAML资源,编译时构建任务将XamlWriter的输出传输到XamlXmlReader到&#34;编译&#34; XAML。在运行时,BamlWriter的输入通过管道传输到BamlReader以创建或修复&#34;根对象。

        一旦理解了所有这些,您就可以开始认识到XAML是一种强大的序列化和持久化格式,而不仅仅是用于构建UI的语言。