深入浅出WPF——如何实现类的Property与标签的Attribute映射

深入浅出WPF——如何实现类的Property与标签的Attribute映射

小序:

咳咳,我知道这个标题不太好让人明白。

它的意思是这样——我们知道,用C#代码编写的类也可以在XAML文档里声明实例,声明之后我们可以使用XAML的Attribute对实例的Property进行赋值。但XAML标签的Attribute只能接受string类型的值,如果像int、double这类简单的值还好办,如果是复杂的值呢?我们应该怎么办?

正文:

我们知道,XAML标签会对应一个实例。如果在XAML里初始化实例的属性,我们有两种语法可以选择:

  • 直接使用Attribute="Value"的方法
  • 属性元素(Property Element)方法

今天我们讨论的核心就是——第一种方法是如何实现的。

先看一个例子。我为一个Grid设置了矢量渐变画刷:

  1. <Grid>
  2. <Grid.Background>
  3. <LinearGradientBrushStartPoint="0,0"EndPoint="1,1">
  4. <GradientStopOffset="0"Color="White"/>
  5. <GradientStopOffset="0.5"Color="LawnGreen"/>
  6. <GradientStopOffset="1"Color="White"/>
  7. </LinearGradientBrush>
  8. </Grid.Background>
  9. </Grid>

效果是这样:

深入浅出WPF——如何实现类的Property与标签的Attribute映射

请大家注意StartPoint="0,0"EndPoint="1,1这两个Attribute,它们实际上对应的是实例的两个属性。如果查看MSDN,你会发现这两个属性的数据类型是Point而不是String。那么,一个string类型值是如何被转换成Point类型值的呢?

让我们自己动手DIY一个!

首先,我们准备了一个类:

  1. publicclassHuman
  2. {
  3. publicstringName{get;set;}
  4. publicHumanChild{get;set;}
  5. }

这个类具有两个属性

  • string类型的Name
  • Human类型的Child

现在我的期望是,如果我在XAML里这样写:

  1. <Window.Resources>
  2. <local:Humanx:Key="human"Child="ABC"/>
  3. </Window.Resources>

则能够为Human实例的Child赋一个Human类型的值,并且Child.Name就是这个字符串的值。

我们先看看直接写行不行……

我在UI上添加了一个按钮button1,并在它的Click事件处理器里写上:

  1. privatevoidbutton1_Click(objectsender,RoutedEventArgse)
  2. {
  3. Humanh=(Human)this.FindResource("human");
  4. MessageBox.Show(h.Child.Name);
  5. }

编译没有问题,但在我点击按钮之后程序抛出异常——告诉我Child不存在。那我们应该怎么做呢?

办法是使用TypeConverter和TypeConverterAttribute这两个类。

首先,我们要从TypeConverter类派生出自己的类,并重写它的一个ConvertFrom方法。这个方法有一个参数名为value,这个值就是在XAML文档里为它设置的值。我们要做的就是把这个值“翻译”成合适类型的值、赋给对象的属性:

  1. publicclassStringToHumanTypeConverter:TypeConverter
  2. {
  3. publicoverrideobjectConvertFrom(ITypeDescriptorContextcontext,System.Globalization.CultureInfoculture,objectvalue)
  4. {
  5. if(valueisstring)
  6. {
  7. Humanh=newHuman();
  8. h.Name=valueasstring;
  9. returnh;
  10. }
  11. returnbase.ConvertFrom(context,culture,value);
  12. }
  13. }

有了这个类还不够,还要使用TypeConverterAttribute这个特征类把StringToHumanTypeConverter这个类“粘贴”到作为目标的Human类上。

  1. [TypeConverterAttribute(typeof(StringToHumanTypeConverter))]
  2. publicclassHuman
  3. {
  4. publicstringName{get;set;}
  5. publicHumanChild{get;set;}
  6. }

因为特征类在使用的时候可以省略Attribute这个词,所以我们也可以写成:

  1. [TypeConverter(typeof(StringToHumanTypeConverter))]
  2. publicclassHuman
  3. {
  4. publicstringName{get;set;}
  5. publicHumanChild{get;set;}
  6. }

但这样写,我们需要认清写在方括号里的是TypeConverterAttribute而不是TypeConverter。

完成之后,再次点击按钮,我们想要的结果就出来了!

深入浅出WPF——如何实现类的Property与标签的Attribute映射

TO BE CONTINUE...