WPF通常用Xaml格式创建对象树。您还可以使用XamlWriter类进行反方向操作——将对象树写入Xaml。

对于XamlWriter来说,将对象转换成良好的Xaml表示形式通常很容易。但是,您不能总是通过查看对象的属性就知道如何将对象写入Xaml。当你创建一个新类时,你需要做一些事情,使你的类在XamlWriter中工作得更好。

XamlWriter 是什么?

XamlWriter是一个允许你从对象创建Xaml的类。对象的类型可以不是WPF特有的,您可以对任何托管类型使用XamlWriter和XamlReader,例如在c#、VB中创建的那些类型。

假设我们用这样的代码片段创建了一个Button对象:

Button button = new Button();

button.Width = 100.0;

button.Background = Brushes.Red;

想把它写成Xaml,可以用这样的代码:

string xamlString = XamlWriter.Save( button );

其结果xamlString 的内容是这样的:

<Button xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" Width="100" Background="Red"/>

长的xmlns Uri用于将标记映射到程序集。这很有用,但是有点分散注意力,所以我先不讲了。因此它只是:<Button Width="100" Background="Red"/>

非常简单——将对象的类型写成标记,将其属性写成特性。

本例中的红色是一个简单的SolidColorBrush,它很容易表示为一个特性。但是使用LinearGradientBrush要困难得多——这需要更多的XML。例如:

<Button Width="100">

  <Button.Background>

      <LinearGradientBrush>

          <GradientStop …="" />

        ...

      </LinearGradientBrush>

    </Button.Background>

</Button>

在本例中,我们使用Xaml的属性元素语法,Background标记表明该标记的内容实际上是Background属性的一个值,在本例中是一个LinearGradientBrush。

这简单地展示了XamlWriter—Save方法获取对象并将它们转换为Xaml。基本的算法是将对象的类型名写入标记,然后将对象的属性值写入属性(如果它们是简单的属性)或属性元素标记下的嵌套标记。

顺便说一下,如果您已经对Xaml有过很多了解,您可能已经知道有时属性元素标记会被省略。例如,对于Grid,子属性是它的内容属性,因此:

<Grid>

    <Grid.Children>

        <Button />

        <Button />

    </Grid.Children>

</Grid>

可以简写成:

<Grid>

     <Button />

     <Button />

</Grid>

这是基本的Xaml语法(基于[ContentProperty]属性),但这里的重点是XamlWriter理解它,并在可能的情况下编写更简单的语法。

XamlWriter的限制

不是所有的类都可以用Xaml表示。例如,Xaml不能(在大多数情况下)表示类型,除非它们有默认构造函数,因此XamlWriter也不会编写这样的类型。另一个例子是,Xaml不能直接表示对象的图形。

其他限制:

1,字段。只写属性,不写字段。

2,只读属性(有setter而没有getter的属性)。因为不可以从Xaml设置只读属性了。但是,可以使用下面描述的[DesignerSerializationVisibility]属性覆盖它。

3,内啊/私有属性。只有public属性才可以写。

4,内部/私有类。与前面类似,只编写pulice类。实际上,尝试编写非公共类会引发异常。

5,事件监听器:例如,button.Click+=OnClick(),您不会得到<Button Click="OnClick"/>,单击处理程序将被删除。

最后,如果您使用XamlReader读取Xaml,然后使用XamlWriter编写它,您将不会得到完全相同的XML。另外,一些标记扩展(如{x:Static})在加载时由XamlReader解析,标记扩展本身被丢弃,因此XamlWriter无法重新生成它。

什么样的properties可以写成Xaml

对象被写为标签,属性被写为属性或属性元素标签。在大多数情况下,你不需要做任何事情,就可以写出正确的属性。但也许不是,在这种情况下,您可以在类定义中做一些工作来获得更多的控制权。有几个不同的选择…

1,不写缺省值。一般来说,如果属性是默认值,那么将它写出来是多余的。类型作者(即定义类或结构的人)可以使用[DefalutValue]属性让XamlWriter知道属性的默认值。例如,在下面的例子中,如果MyClass对象的值为0,那么MyProperty就不会被写出来:

class MyClass

{

[DefaultValue(0)]

public int MyProperty

{

get … set …

}

}

但是,如果属性由DependencyProperty支持,则此规则略有不同。在这种情况下,属性只有在实际设置时才会被写入。例如,下面的按钮不是在按钮本身上获取其背景属性设置,而是从按钮的样式中获取:

<Window>

<Window.Resources>

<Style TargetType="Button">

<Setter Property="Background" Value="Red" />

</Style>

</Window.Resources>

<Button Name="button1">Click</Button>

</Window>

在这个例子中,代码button1.Background会得到一个红色的画笔,但是如果您使用XamlWriter编写它,您只会得到:

<Button Name="button1">Click</Button>

2,写只读属性。通常没有必要将只读属性的值写入Xaml,因为它不可能从Xaml加载回来,因为Xaml不能设置只读属性。但是,在一种情况下,Xaml可以设置为只读属性,这是集合类型属性。在这种情况下,它不是设置属性本身,而是添加到集合中。例如,Grid.Children是只读的,但是您仍然可以使用:

<Grid>

<Grid.Children>

<Button …/>

<Button …/>

</Grid.Children>

</Grid>

只读集合属性通常可以写入OK,但XamlWriter默认情况下不写入它们。要覆盖它,您可以使用[DesignerSerializationVisibility]属性在类定义中覆盖它:

class Grid

{

[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]

public UIElementCollection Children

{

get { … }

}

...

}

3,不写可读写属性。相反的情况是,如果您有一个读/写属性,并且看起来可以写入Xaml,但是您不希望它是这样的。例如,在下面的类中,DoorChanges属性是一个运行时计数器,不需要持久化。这里我们使用[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]来阻止它被写入:

class Car

{

int _doorChanges = 0;

[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]

public int DoorChanges

{

get { return _doorChanges; }

set { _doorChanges = value; }

}

...

}

4,在运行时决定是否编写。DesignerSerializationVisibility属性是一种方法,用于声明性地确定某个属性是否由XamlWriter编写。XamlWriter还可以设定为在调用期间做出运行时决策。为此,需要在属性中添加“ShouldSerializeProperty”方法,其中“property”是属性的名称。例如:

class MyClass

{

public int Foo

{

get { return _foo; }

set { _foo = value; }

}

public bool ShouldSerializeFoo()

{

if( …  )

return false;

else

return true;

}

...

}

如果属性是由DependencyProperty支持的,那么在这个运行时检查中还有一个额外的选择。在这种情况下,您仍然可以使用这个ShouldSerializeProperty模式,或者可以覆盖DependencyObject.ShouldSerializeProperty虚方法。例如:

override bool ShouldSerializeProperty( DependencyProperty dp )

{

if( dp == FooProperty )

{

if( … )

return false;

}

return true;

}

使用类型转换器转换property编写入Xaml

最后一部分是关于是否应该将属性写入Xaml。一旦决定了应该这样做,下一个决定就是如何编写它——作为属性(如下面例子中的Width),或者标记(如下面的Background ):

<Button Width="100">

  Click

  <Button.Background>

    <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">

      <LinearGradientBrush.GradientStops>

        <GradientStop Color="Blue" Offset="0"/>

        <GradientStop Color="Red" Offset="1"/>

      </LinearGradientBrush.GradientStops>

    </LinearGradientBrush>

  </Button.Background>

</Button>

如何将宽度整数转换为字符串“100”?答案是类型转换器。类型转换器与类型相关联,用于与其他类型进行转换。在加载Xaml时,类型转换器用于从字符串进行转换,在编写Xaml时,类型转换器用于将其转换为字符串。

double类型有一个内置的类型转换器,上面使用它创建字符串“100”。类似地,string、float、int和所有其他基本类型都有内置的类型转换器。Enum还有一个内置的类型转换器,您可以在其中简单地使用字段名。这是所有标准类型转换器的功能;对于Xaml来说,这并不是什么新鲜事。

但是由于Xaml可以识别类型转换器,这意味着您可以为您的类创建自己的类型转换器:

[TypeConverter( typeof( FooConverter ))]

class Foo

{

...

}

这样,如果另一个类具有这种类型的属性:

class Bar

{

public Foo Foo

{

get ... set ...

}

}

它可以在Xaml中作为一个属性引用,使用类型转换器:

<Bar Foo="20" />

不要忘记,虽然在类型转换器中需要实现的只是ConvertFrom和CanConvertFrom,但是还需要实现ConvertTo和XamlWriter使用的CanConvertTo。

此外,除了向类注册类型转换器外,还可以在属性上注册它。如果在属性和属性的类型上都指定了类型转换器,则属性上的转换器优先。所以:

class Bar

{

    [TypeConverter( typeof( SpecialFooConverter ))]

    public Foo Foo

    {

        get … set ...

    }

}

Foo类有一个类型转换器,但是在Bar上,Foo用它自己的类型转换器。

用ValueSerializer转换property写入Xaml

XamlWriter还引入了一种写属性值的新方法,称为ValueSerializer,它类似于类型转换器。

在WPF中,将写属性最常见情况之一是画笔。这是因为每个人都希望能够使用一个简单的属性将按钮的Background 设置为“红色”,而不是冗长的 <Button.Background><SolidColorBrush …/></Button.Background>

但是Brush是一个很不简单的问题,因为您可以将Brush写入Xaml的方式是不明确的。这是因为Brush是SolidColorBrush的基类,它提供简单的颜色,以及更复杂的画笔,如LinearGradientBrush、ImageBrush、VisualBrush等。如果将类型转换器放在Brush上,XamlWriter调用CanConvertTo方法来查看是否可以将其转换为字符串,那么答案肯定是“也许”。这是因为如果值是一个SolidColorBrush,您可以将其转换为字符串(可以是像“Red”这样的简单字符串,也可以使用“##AARRGGBB”语法),但是其他画笔,如LinearGradientBrush,不能转换为属性字符串。问题是类型转换器没有给你做出这个决定的方法;当CanConvertTo被调用时,您不会得到对正在编写的实际Brush对象的引用。

XamlWriter的解决方案是ValueSerializer。值序列化器很像类型转换器,因为它有ConvertTo/From、CanConvertTo/From方法。您还可以使用[ValueSerializer]属性指向它,就像您使用[TypeConverter]属性指向类型转换器一样。值序列化器与类型转换器的一个不同之处在于,它只将对象转换为字符串,而不是将对象转换为任何类型。另一个关键区别是,它可以把字符串反序列化为对象。

在WPF中,画笔类是这样的:

[TypeConverter(typeof(BrushConverter))]

[ValueSerializer(typeof(BrushValueSerializer))]

public abstract class Brush : ...

{

...

}

BrushValueSerializer看起来是这样的:

class BrushSerializer : ValueSerializer

{

  ...

  public override bool CanConvertToString(object value, IValueSerializerContext context)

  {

      if( value is SolidColorBrush )

          return true;

      else

          return false;

  }

  ...

}

注意,Brush既有类型转换器,也有值序列化器。这是因为XamlReader不识别值序列化器;它只使用类型转换器。另一方面,当XamlWriter看到值序列化器和类型转换器都可用时,它会选择值序列化器。

最后:

您可以控制类上的哪些属性将由XamlWriter编写,并且可以控制它们是否作为属性或属性元素写入Xaml。大多数情况下,即使您不做任何事情,您创建的类也可以很好地与XamlWriter一起工作。到目前为止,您遇到的最可能的问题是默认情况下不会写入只读集合属性。所以你最可能要做的就是设置[DesignerSerializationVisibility(DesignerSerializationVisibility.Conten)

转载于:https://www.cnblogs.com/TianPing/p/10387892.html

XamlWriter-将对象树写入Xaml相关推荐

  1. python在读写文件之前需要创建文件对象-Python对象序列化写入文件对象

    1.创建Python文件对象的读写模式(r,w模式)与创建Java输入输出流: FileInputStream inputStream=new FileInputStream(new File(&qu ...

  2. 6-Qt6对象树及内存管理

    把类的对象组织成树形结构,这种树形结构也称为对象树,Qt 使用对象树来管理 QObject 及其子类的对象. 重要:当父对象析构的时候,这个列表中的所有对象也会被自动逐级析构. 如下图树形,当Pare ...

  3. [Qt教程] 第43篇 进阶(三)对象树与拥有权

    [Qt教程] 第43篇 进阶(三)对象树与拥有权 楼主  发表于 2013-9-12 16:39:33 | 查看: 255| 回复: 1 对象树与拥有权 版权声明 该文章原创于Qter开源社区 导语 ...

  4. QT_4_QpushButton的简单使用_对象树

    QpushButton的简单使用 1.1 按钮的创建 QPushButton *btn = new QPushButton; 1.2 btn -> setParent(this);设置父窗口 1 ...

  5. QT对象树、信号和槽机制

    文章目录 一 .对象树是什么? 二.信号和槽的基本概念 2.1 信号 2.2 槽 2.3 松散耦合 2.4 特点 三.示例 总结 一 .对象树是什么? 对象树是由父类和若干子类对象组成,而子类也可以由 ...

  6. Qt工作笔记-通过 对象树 或 delete this 释放对象

    目录 背景 实例 背景 今天在写代码的时候,new出的界面,没有delete,因为是dialog,可以用exec() 把后面的界面阻塞掉,但个人并不喜欢这样,毕竟觉得强扭的瓜不甜!不想让后面的那个界面 ...

  7. Qt——P7 对象树

    mypushbutton.cpp #include "mypushbutton.h" #include<QDebug>MyPushButton::MyPushButto ...

  8. 【Qt教程】1.5 - Qt5内存回收机制-对象树、窗口坐标系

    1.Qt内存回收机制 - 对象树 (做了解,懂就可以,示例看视频) 当创建的对象在堆区时,如果指定的父亲是 QObject派生下来的类 或者 QObject子类派生下来的类,可以不用管理释放的操作,对 ...

  9. 最新QT从入门到实战完整版(07 对象树)

    最新QT从入门到实战完整版(07 对象树) 一.07 对象树 来自 一.07 对象树 看到我们创建了一个按钮,然后并且让它显示到窗口中啊,那下边大家看一下我刚才创建的按钮啊.都写的是new啊,都写的是 ...

最新文章

  1. mysql为什么要重建索引_Oracle 重建索引的必要性
  2. zookeeper配置
  3. 条件控制(if ) ( case)
  4. copy a random link
  5. 传奇落幕!杰克·韦尔奇给管理者的10句箴言,句句经典!
  6. lampp mysql 等待响应时间很长_Apache 打开网页的时候等待时间过长的解决方案
  7. redis 条件查询
  8. 深度学习:卷积神经网络(详解版)
  9. elementui的横向滚动,element-ui中隐藏组件el-scrollbar的使用
  10. 抖音短视频账号运营方案
  11. 这就是iPhone 6的屏幕?
  12. 端游开发用什么技术可以让用户更短时间内体验游戏?端游分发
  13. 面试被问:你了解的海康威视是一家怎样的公司?
  14. java菜鸟2:java指令
  15. 条件自信息量与互信息量的区别
  16. i春秋网鼎杯网络安全大赛advanced题目writeup
  17. Atlas——数据治理工具的使用
  18. CmsWing源码分析(8) 栏目
  19. 敏捷画卷:中国软件史的精彩侧影
  20. 电脑提示msvcp110.dll丢失怎样修复?教程

热门文章

  1. 原子变量的原理与应用
  2. click quickstart
  3. Java MyBatis 别名
  4. java 屏幕键盘io
  5. 编程语言对比 主程序传参
  6. 2.11 计算机视觉现状
  7. pandas 链接数据库
  8. 华为vrrp默认优先级_华为VRRP综合配置
  9. RVC使用指南(二)-集群管理
  10. Oracle学习总结(2)——Oracle数据库设计总结(三大范式)