XAML中的Attribute只接受String类型的值,例如:
<Grid Background="Red"/>
问题来了,XAML解释器是如何将这个"Red"解释为Backgroud可接受类型的?事实上,这里使用了类型转换器(TypeConverter)。所谓类型转换器,顾名思义就是从一种类型转换为另一种类型的手段。简而言之就是将String类型的Red转换成Color类型了。
这里举两个简单示例来阐述如何使用TypeConvert。
示例1:Name转换为Human
先定义一个Human类,包含两个字段 Name 和 Child。
class Human
{
private string name;
private Human child;
public string Name
{
get { return name; }
set { name = value; }
}
public Human Child
{
get { return child; }
set { child = value; }
}
public Human()
{
Name = string.Empty;
Child = null;
}
public Human(string name)
:this ()
{
Name = name;
}
}
然后需要定义TypeConverter这个类。首先有个基本概念得明确,就是类型转换器的Source和Target。对于这个例子,使用String类型作为Source,Human作为Target,当然反过来也没有任何问题。于是就新建一个类型转换器 NameToHumanTypeConverter~
第一点:任何的类型转换器都必须继承于TypeConverter类。
第二点:需要重写方法
- CanConvertFrom
- ConvertFrom
- CanConverTo
- ConvertTo
注意一个方向的问题,From(String -> Human),To(Human -> String)。可以看到类型转换器是支持双向操作的,这个示例只用到From就足以。
public class NameToHumanTypeConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, System.Type sourceType)
{
if (sourceType == typeof(string))
return true;
else
return base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
{
if (value != null && value is string)
{
Human human = new Human();
human.Name = (string)value;
return human;
}
else
{
return base.ConvertFrom(context, culture, value);
}
}
}
接下来将这个类型转换器以Attribute的形式添加到Human类,添加的目的是为了实现自动转化,第二个示例会说明这个问题。
[TypeConverter(typeof(NameToHumanTypeConverter))]
class Human
{
// ...
}
修改XAML及后台事件代码。
<Window x:Class="类型转换01.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:codes="clr-namespace:类型转换01"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<codes:Human x:Key="man" Name="Louis" Child="Liu"></codes:Human>
</Window.Resources>
<Grid>
<Button Click="Button_Click"></Button>
</Grid>
</Window>
private void Button_Click(object sender, RoutedEventArgs e)
{
Human human = this.FindResource("man") as Human;
MessageBox.Show(string.Format("Man: {0}\nChild: {1}", human.Name, human.Child.Name));
}
运行程序查看效果:
可以看到成功地将 Louis 和 Liu 转换成了两个Human对象。
示例2:Coordinate转换为String
大部分内容和示例1一致,这个示例仅仅是为了说明给类添加类型转换器的必要性,直接上代码:
若要使用如下的TypeDescriptor.GetConverter这个静态方法,则必须给Coordinate类附加类型转换器这个Attribute。
static void Main(string[] args)
{
CoordinateToStringTypeConverter converter = new CoordinateToStringTypeConverter();
Coordinate testCoordinate = new Coordinate(1.2, 3.4);
string resultString = (string)converter.ConvertFrom(testCoordinate);
Console.WriteLine(resultString);
resultString = (string)TypeDescriptor.GetConverter(typeof(Coordinate)).ConvertFrom(testCoordinate);
Console.WriteLine(resultString);
string testString = "5.6,7.8";
Coordinate resultCoordinate = (Coordinate)converter.ConvertTo(testString, typeof(Coordinate));
Console.WriteLine(resultCoordinate);
resultCoordinate = (Coordinate)TypeDescriptor.GetConverter(typeof(Coordinate)).ConvertTo(testString, typeof(Coordinate));
Console.WriteLine(resultCoordinate);
}
另外在MSDN上看到一段内容,说是Markup Extension的结果不能再作为TypeConverter的Source使用,原文如下:
Markup extensions and type converters fill orthogonal roles in terms of XAML processor behavior and the scenarios that they are applied to. Although context is available for markup extension usages, type conversion behavior of properties where a markup extension provides a value is generally is not checked in the markup extension implementations. In other words, even if a markup extension returns a text string as its ProvideValue output, type conversion behavior on that string as applied to a specific property or property value type is not invoked, Generally, the purpose of a markup extension is to process a string and return an object without any type converter involved.