我是WPF菜鸟之(5) --- 澄清Attribute与Property 及Attribute与Property的映射实现

在这里,有必要把Attribute和Property这两个词仔细地辨别一下。
这两个词的混淆由来已久。混淆的主要原因就是大多数中文译本里既把Attribute译为“属性”,也把Property译为“属性”。其实,这两个词所表达的不是一个层面上的东西。
Property 属于面向对象理论范畴。在使用面向对象思想编程的时候,我们常常需要对客观事物进行抽象,再把抽象出来的结果封装成类,类中用来表示事物状态的成员就是 Property。比如我要写一个模拟赛车的游戏,那么必不可少的就是对现实汽车的抽象。现实中的汽车身上会带有很多数据,但在游戏中我可能只关心它的长 度、宽度、高度、重量、速度等有限的几个数据,同时,我还会把汽车“加速”、“减速”等一些行为也提取出来并用算法模拟——这个过程就是抽象(结果是 Car这个类)。显然,Car.Length、Car.Height、Car.Speed等表达的是汽车当前处在一个什么状态,而 Car.Accelerate()、Car.Break()表达的是汽车能做什么。因此,Car.Length、Car.Height、 Car.Speed就是Property的典型代表,将Property译为“属性”也很贴切。总结一句话就是:Property(属性)是针对对象而言 的。
 Attribute则是编程语言文法层面的东西。比如我有两个同类的语法元素A和B,为了表示A与B不完全相同或者A与B在用法上有些区 别,这时候我就要针对A和B加一些Attribute了。也就是说,Attribute只于语言层面上的东西相关——与抽象出来的对象没什么关系。因为 Attribute是为了表示“区分”的,所以我喜欢把它译为“特征”。C#中的Attribute就是这种应用的典型例子——我们可以为一个类添加 Attribute,这个类的类成员中有很多Property——显然Attribute只是用来影响类在程序中的用法而Property则对应着抽象对 象身上的性状,它们根本不是一个层面上的东西。
 习惯上,英文中把标签式语言中表示一个标签特征的“名称-值”对称为Attribute。如果恰 好我们又是用一种标签语言在进行面向对象编程,这时候两个概念就有可能混淆在一起了。实际上,使用能够进行面向对象编程的标签式语言只是把标签与对象做了 一个映射,同时把标签的Attribute与对象的Property也做了一个映射——针对标签,我们还是叫Attribute,针对对象,我们还是叫 Property,仍然不是一个层面上的东西。而且,标签的Attribute与对象的Property也不是完全映射的,往往是一个标签所具有的 Attribute多于它所代表的对象的Property。
 因为XAML是用来在UI上绘制控件的,而控件本身就是面向对象抽象的产物,所以XAML标签的Attribute里有就一大部分是与控件对象的Property互相对应的。当然,这还意味着XAML标签还有一些属性并不对应控件对象的Property。

===========================================================================================

如何实现类的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.             <LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
  4.                 <GradientStop Offset="0" Color="White"/>
  5.                 <GradientStop Offset="0.5" Color="LawnGreen"/>
  6.                 <GradientStop Offset="1" Color="White"/>
  7.             </LinearGradientBrush>
  8.         </Grid.Background>
  9.     </Grid>
效果是这样:
请大家注意StartPoint="0,0" EndPoint="1,1这两个Attribute,它们实际上对应的是实例的两个属性。如果查看MSDN,你会发现这两个属性的数据类型是Point而不是String。那么,一个string类型值是如何被转换成Point类型值的呢?
 
让我们自己动手DIY一个!
 
首先,我们准备了一个类:
 
  1.     public class Human
  2.     {
  3.         public string Name { getset; }
  4.         public Human Child { getset; }
  5.     }
这个类具有两个属性
  • string类型的Name
  • Human类型的Child
现在我的期望是,如果我在XAML里这样写:
  1.     <Window.Resources>
  2.         <local:Human x:Key="human" Child="ABC"/>
  3.     </Window.Resources>
则能够为Human实例的Child赋一个Human类型的值,并且Child.Name就是这个字符串的值。
我们先看看直接写行不行……
我在UI上添加了一个按钮button1,并在它的Click事件处理器里写上:
  1.         private void button1_Click(object sender, RoutedEventArgs e)
  2.         {
  3.             Human h = (Human)this.FindResource("human");
  4.             MessageBox.Show(h.Child.Name);
  5.         }
编译没有问题,但在我点击按钮之后程序抛出异常——告诉我Child不存在。那我们应该怎么做呢?
办法是使用TypeConverter和TypeConverterAttribute这两个类。
 
首先,我们要从TypeConverter类派生出自己的类,并重写它的一个ConvertFrom方法。这个方法有一个参数名为value,这个值就是在XAML文档里为它设置的值。我们要做的就是把这个值“翻译”成合适类型的值、赋给对象的属性:
  1.     public class StringToHumanTypeConverter : TypeConverter
  2.     {
  3.         public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
  4.         {
  5.             if (value is string)
  6.             {
  7.                 Human h = new Human();
  8.                 h.Name = value as string;
  9.                 return h;
  10.             }
  11.             return base.ConvertFrom(context, culture, value);
  12.         }
  13.     }
有了这个类还不够,还要使用TypeConverterAttribute这个特征类把StringToHumanTypeConverter这个类“粘贴”到作为目标的Human类上。
  1.     [TypeConverterAttribute(typeof(StringToHumanTypeConverter))]
  2.     public class Human
  3.     {
  4.         public string Name { getset; }
  5.         public Human Child { getset; }
  6.     }
因为特征类在使用的时候可以省略Attribute这个词,所以我们也可以写成:
  1.     [TypeConverter(typeof(StringToHumanTypeConverter))]
  2.     public class Human
  3.     {
  4.         public string Name { getset; }
  5.         public Human Child { getset; }
  6.     }
但这样写,我们需要认清写在方括号里的是TypeConverterAttribute而不是TypeConverter。
 
完成之后,再次点击按钮,我们想要的结果就出来了!
 

posted on 2014-04-15 14:50  pcshell  阅读(627)  评论(0编辑  收藏  举报

导航