XamlWriter-将对象树写入Xaml
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)
XamlWriter-将对象树写入Xaml的更多相关文章
- C语言的引用计数与对象树
引用计数与对象树 cheungmine 2013-12-28 0 引言 我们经常在C语言中,用指针指向一个对象(Object)的结构,也称为句柄(Handle),利用不透明指针的技术把结构数据封装成对 ...
- Qt对象树
Qt提供了一种机制,能够自动.有效的组织和管理继承自QObject的Qt对象,这种机制就是对象树.子对象动态分配空间不需要释放.
- QtCore是Qt的精髓(包括五大模块:元对象系统,属性系统,对象模型,对象树,信号槽)
作者:小豆君的干货铺链接:https://www.zhihu.com/question/27040542/answer/218384474来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业 ...
- QT +坐标系统 + 自定义控件 + 对象树的验证(自动进行析构)_内存回收机制
通过创建一个新的按钮类,来进行析构函数的验证,即对象树概念的验证.当程序结束的时候会自动的调用析构函数, 验证思路: 要验证按钮会不会自动的析构,(即在QPushButton类里面的析构函数添加qDe ...
- QT_4_QpushButton的简单使用_对象树
QpushButton的简单使用 1.1 按钮的创建 QPushButton *btn = new QPushButton; 1.2 btn -> setParent(this);设置父窗口 1 ...
- Qt对象模型之二:对象树与元对象系统
一.对象树的概念 Qt中使用对象树(object tree)来组织和管理所有的QObject类及其子类的对象.当创建一个QObject时,如果使用了其他的对象作为其父对象(parent),那么这个 Q ...
- 【转载】 C#中手动创建一个DataTable对象并写入数据
在C#操作集合数据的过程中,有时候需要手动创建一个DataTable对象,并手动设置DataTable对象的Columns列名等信息,最后再往手动创建的DataTable对象中写入相应的数据信息,此时 ...
- QT从入门到入土(二)——对象模型(对象树)和窗口坐标体系
摘要 我们使用的标准 C++,其设计的对象模型虽然已经提供了非常高效的 RTTI 支持,但是在某些方面还是不够灵活.比如在 GUI 编程方面,既需要高效的运行效率也需要强大的灵活性,诸如删除某窗口时可 ...
- qt 中的对象树
本节内容讲解了什么是对象树以及其所带来的 GUI 编程好处.最后说明了在对象树中析构顺序问题并举了个特殊的例子,来说明平时编程中需要注意的一个点. 什么是对象树? 我们常常听到 QObject 会用对 ...
随机推荐
- Java笔试面试题整理第六波(修正版)
转载至:http://blog.csdn.net/shakespeare001/article/details/51330745 作者:山代王(开心阳) 本系列整理Java相关的笔试面试知识点,其他几 ...
- JDK1.7 高并发下的HashMap
HashMap的容量是有限的.当经过多次元素插入,使得HashMap达到一定饱和度时,Key映射位置发生冲突的几率会逐渐提高. 这时候,HashMap需要扩展它的长度,也就是进行Resize. 影响发 ...
- C# 设置Excel数字格式
数字格式使指能够控制Excel单元格中数字如何显示的格式字符串.例如,我们可以对数字12345应用数字格式“0.00”,使之显示为“12345.00”.在例如对数字12345应用“¥0.00”格式,使 ...
- 14-background
一.background-color:设置该元素的背景颜色 一共有三种:单词.rgb表示法.十六进制表示法 1.rgb:红色 绿色 蓝色 三原色 光学显示器,每个像素都是由三原色的发光原件组成的,靠明 ...
- 定时器和函数的使用初级------移动一个div元素
在页面的动画效果中,经常有看到某个小块从一个地方移动到另一个地方的现象,现在,我们也来自己做一个这样的小动画,涉及到的基础包括定时器的使用和函数的使用 例如,我们要实现一个小方块从左面移动到右面,然后 ...
- css:元素水平垂直居中的多种方式
CSS元素(文本.图片)水平垂直居中方法 1.text-align:center; 2.margin:0 auto; 3.display:inline-block; + text-align:ce ...
- linux 硬链接 软链接
硬链接 链接文件的INode和源文件相同,删除互不影响,不支持跨分区,不支持目录 软链接 链接文件有单独的INode,其Block中存储源文件的INode信息,相当于windows中 的快捷方式 ln ...
- SSM商城项目(十)
1. 学习计划 1.使用freemarker实现网页静态化 a)Freemarker的使用方法 b)Freemarker模板的语法 c)Freemarker整合springmvc 2.Active ...
- Nginx搭建
Nginx nginx是一个开源的,支持高性能,高并发的www服务和代理服务软件. nginx因具有高并发(特别是静态资源),占用系统资源少等特性,且功能丰富而逐渐流行起来. nginx不但是一个优秀 ...
- HLSL ddx / ddy
[HLSL ddx / ddy] 在光栅化的时刻,GPUs会在同一时刻并行运行很多Fragment Shader,但是并不是一个pixel一个pixel去执行的,而是将其组织在2x2的一组pixels ...