WPF学习:4.类型转换和标记扩展

  在上一章,主要介绍了Border和Brush,这一章主要介绍下类型转换和标记扩展。相关代码链接如下:

  https://files.cnblogs.com/keylei203/4.WPFSampleDemo.zip

Type Converter

  在XAML中,所有的对象都是字符串,XAML解析器通过类型转换器跨越字符和非字符的鸿沟,类型转换器就是将这些字符串转换为相应的CLR对象。

  

  所有类型转换器都是派生自TypeConverter,派生的类有100多个,比较常用的比如BrushConverter,就可以实现从字符串转换成相应的画刷,又如Margin,Margin=10,20,0,30,解析器会自动按照左,上,右,下的顺序将数据转换为宽度数据。

  TypeConverter都4个重要方法是CanConvertToCanConvertFromConvertToConvertFrom

  CanConvertTo:检测CLR对象是否能转换成相应的字符串。

  CanConvertFrom:检测能否从字符串转换成相应的CLR对象。

  ConvertTo:将CLR对象转换成相应的字符串。

  ConvertFrom:从字符串转换成相应的CLR对象。

  XAML解析器解析任何属性值时都包含两部分信息:Value Type-决定了字符串转化后的类型。Actual Value-属性值大小。解析器通过两个步骤来查找类型转换器:

  (1)检查属性声明查找TypeConvert特性,如窗口的Width和Height属性(派生自FrameworkElement)前面都会声明一个类型转换器。下面代码通过Reflector查看到的FrameworkElement的源代码片段:

1 public class FrameworkElement....
2 {
3     [......TypeConverter(typeof(LengthConverter))] 
4     public double Height{get;set;}
5     [......TypeConverter(typeof(LengthConverter))]
6     public double Width{get;set;}         
7 }

  (2)如果属性声明中没有TypeConverter特征,XAML解析器会检查对应的数据类型的类的声明。如按钮Background属性声明是Brush,并在Brush类头部就声明了一个BrushConverter转换器,这样在设置Background属性时XAML解析器会自动应用该转换器。下面代码为用过Reflector查看到的Brush片段。

1 [......TypeConverter(typeof(BrushConverter))......
2 public abstract class Brush:Animatable

Custom TypeConverter

  建立一个CustomTypeConverter必须建立一个新类,在我的例子中,我建立了一个具有Latitude和Longitude属性的类,这个类实现地理位置。

 1 [global::System.ComponentModel.TypeConverter(typeof(GeoPointConverter))]
 2     public class GeoPointItem
 3     {
 4         public double Latitude { get; set; }
 5         public double Longitude { get; set; }
 6 
 7         public GeoPointItem()
 8         {
 9         }
10 
11         public GeoPointItem(double lat, double lon)
12         {
13             this.Latitude = lat;
14             this.Longitude = lon;
15         }
16 
17         public static GeoPointItem Parse(string data)
18         {
19             if (string.IsNullOrEmpty(data)) return new GeoPointItem();
20 
21             string[] items = data.Split(',');
22             if (items.Count() != 2)
23                 throw new FormatException("GeoPoint should have both latitude 
24                 and longitude");
25 
26             double lat, lon;
27             try
28             {
29                 lat = Convert.ToDouble(items[0]);
30             }
31             catch (Exception ex) { 
32                 throw new FormatException("Latitude value cannot be converted", ex); 
33             }
34 
35             try
36             {
37                 lon = Convert.ToDouble(items[1]);
38             }
39             catch (Exception ex) { 
40                 throw new FormatException("Longitude value cannot be converted", ex); 
41             }
42 
43             return new GeoPointItem(lat, lon);
44         }
45 
46         public override string ToString()
47         {
48             return string.Format("{0},{1}", this.Latitude, this.Longitude);
49         }
50     }

  上面的类具有Latitude和Longitude两个double型属性,我重写了ToString(),这对于得到一个完整的字符串来说是非常重要的,Parse方法能够解析字符串格式到地理坐标。

  接下俩就是为这个类写一个TypeConverter,将实现4个方法,关键的是ConvertFrom方法,通过GeoPointItem静态方法Parse将字符串转化为正确的类型。

 1 public class GeoPointConverter : global::System.ComponentModel.TypeConverter
 2     {
 3 
 4         //should return true if sourcetype is string
 5         public override bool CanConvertFrom(
 6          System.ComponentModel.ITypeDescriptorContext context, Type sourceType)
 7         {
 8             if (sourceType is string)
 9                 return true;
10             return base.CanConvertFrom(context, sourceType);
11         }
12         //should return true when destinationtype if GeopointItem
13         public override bool CanConvertTo(
14              System.ComponentModel.ITypeDescriptorContext context, Type destinationType)
15         {
16             if (destinationType is string)
17                 return true;
18 
19             return base.CanConvertTo(context, destinationType);
20         }
21         //Actual convertion from string to GeoPointItem
22         public override object ConvertFrom(
23      System.ComponentModel.ITypeDescriptorContext context, 
24          System.Globalization.CultureInfo culture, object value)
25         {
26             if (value is string)
27             {
28                 try
29                 {
30                     return GeoPointItem.Parse(value as string);
31                 }
32                 catch (Exception ex)
33                 {
34                     throw new Exception(string.Format(
35       "Cannot convert '{0}' ({1}) because {2}", value, value.GetType(), ex.Message), ex);
36                 }
37             }
38 
39             return base.ConvertFrom(context, culture, value);
40         }
41 
42         //Actual convertion from GeoPointItem to string
43         public override object ConvertTo(
44          System.ComponentModel.ITypeDescriptorContext context, 
45           System.Globalization.CultureInfo culture, object value, Type destinationType)
46         {
47             if(destinationType == null)
48                 throw new ArgumentNullException("destinationType");
49     
50              GeoPointItem gpoint = value as GeoPointItem;
51 
52             if(gpoint != null)
53             if (this.CanConvertTo(context, destinationType))
54                 return gpoint.ToString();
55             
56             return base.ConvertTo(context, culture, value, destinationType);
57         }
58     }

  接下去是使用这个类型转化器的时候了,建立一个用户控件,添加一个GeoPoint属性:

 1 <Grid>
 2         <Grid.RowDefinitions>
 3             <RowDefinition/>
 4             <RowDefinition/>
 5         </Grid.RowDefinitions>
 6         <Grid.ColumnDefinitions>
 7             <ColumnDefinition/>
 8             <ColumnDefinition/>
 9         </Grid.ColumnDefinitions>
10         <TextBlock Text="Latitude" Grid.Row="0" Grid.Column="0"></TextBlock>
11         <TextBox x:Name="txtlat" MinWidth="40" Grid.Row="0" Grid.Column="1" 
12               TextChanged="txtlat_TextChanged"/>
13         <TextBlock Text="Longitude" Grid.Row="1" Grid.Column="0"></TextBlock>
14         <TextBox x:Name="txtlon" MinWidth="40" Grid.Row="1" Grid.Column="1" 
15              TextChanged="txtlon_TextChanged"/>
16     </Grid>

  控件拥有两个Textboxes显示Latutude和Longitude,当textBox内容发生修改时,GeopointItem值也会相应更改。

 1 public partial class GeoPoint : UserControl
 2 {
 3         public static readonly DependencyProperty GeoPointValueProperty = 
 4        DependencyProperty.Register("GeoPointValue", typeof(GeoPointItem), 
 5              typeof(GeoPoint), new PropertyMetadata(new GeoPointItem(0.0, 0.0)));
 6         public GeoPoint()
 7         {
 8             InitializeComponent();
 9         }
10        
11         public GeoPointItem GeoPointValue
12         {
13             get
14             {
15                 return this.GetValue(GeoPointValueProperty) as GeoPointItem;
16             }
17             set
18             {
19                 this.SetValue(GeoPointValueProperty, value);
20             }
21         }
22 
23         private void txtlat_TextChanged(object sender, TextChangedEventArgs e)
24         {
25             GeoPointItem item = this.GeoPointValue;
26 
27             item.Latitude = Convert.ToDouble(txtlat.Text);
28             this.GeoPointValue = item;
29         }
30 
31         private void txtlon_TextChanged(object sender, TextChangedEventArgs e)
32         {
33             GeoPointItem item = this.GeoPointValue;
34 
35             item.Longitude = Convert.ToDouble(txtlon.Text);
36             this.GeoPointValue = item;
37         }
38 
39         private void UserControl_Loaded(object sender, RoutedEventArgs e)
40         {
41             GeoPointItem item = this.GeoPointValue;
42             this.txtlat.Text = item.Latitude.ToString();
43             this.txtlon.Text = item.Longitude.ToString();
44 
45         }
46     }

  对于窗体,需要添加这个自定义控件,设置它的初值。

1 <converter:GeoPoint x:Name="cGeoPoint" GeoPointValue="60.5,20.5" />

Markup Extension

  MarkupExtension提供一个XAML属性的灵活性,XAML中只要属性值被{}括起,XAML解析器就会认为这是一个扩展标记,而不是一个普通的字符串。

Extension

  在System.Windows.Markup命名空间内已经有了一些标记扩展:  

NullExtention

  是XAML提供的一种标记扩展,表示一个空值,这可能是所有标记扩展中最简单的了。

Content = "{x:Null}"

ArrayExtension

  用来创建一个数组集合。

Values = {x:Array Type=sys:String}

StaticExtension

  返回一个静态字段和引用

Text="{x:Static Member=local:MyClass.StaticProperty}"

  当你定义一个MyClass类的静态属性时,你可以使用它来将属性自动的赋给Text。

TypeExtension

 

TargetType="{x:Type Button}" 

  获得一个button对象的类型

Reference

Text="{x:Reference Name=Myobject}"

  引用一个在XAML中声明的对象,.NET4.0特性

StaticResourceExtension

1 <Grid.Resources>
2 
3 <Color x:Key="rKeyBlack">Black</Color>
4 <SolidColorBrush Color="{StaticResource rKeyBlack}" x:Key="rKeyBlackBrush"/>
5 
6 
7 </Grid.Resources>
8 
9 <TextBlock Background="{StaticResource ResourceKey=rKeyBlackBrush}" />

DynamicResourceExtension

<TextBlock Background="{DynamicResource ResourceKey=rKeyBlackBrush}" />

  似水无痕:http://www.cnblogs.com/keylei203/

 

posted @ 2013-03-12 21:25  似水无痕keylei  阅读(800)  评论(0编辑  收藏  举报