WPF 通用值转换器





5.00/5 (13投票s)
一个简单的 IValueConverter 实现,它利用框架类型转换器在各种源/目标类型之间进行转换
本文提供了一个简单的 IValueConverter 实现,它利用框架类型转换器在各种源/目标类型之间进行转换。 此转换器既可以在绑定中使用,也可以在代码隐藏中使用,以提供更简洁的属性设置器。
引言
XAML 语言的一大特点是它灵活、简洁且富有表现力(是的,我知道 XML 可能有点冗长,但如果您尝试纯粹在代码隐藏中创建一个复杂的 UI,我想您会同意我的观点!)。 例如,您可以通过简单地指定命名颜色来设置矩形的填充
<Rectangle Fill="Green"/)
或者……您可以直接指定 RGB 值
<Rectangle Fill="#00FF00"/)
查看上面的示例,您可能会误以为 Fill
属性的类型是 Color
。WPF 的新手经常发现第一次尝试将 Color
类型的属性绑定到 Fill
属性时并非如此(他们永远不会在代码隐藏中直接设置 Fill
属性,因为那将是一种不可饶恕的罪过!)。 Fill
属性实际上是 Brush
类型,XAML 解析器正在执行一些巧妙的类型转换以使上面的标记工作。
解决将 Color
绑定到 Fill
属性的问题的方案是创建一个值转换器
public class ColorToBrushConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
if (value is Color)
{
return new SolidColorBrush((Color)value);
}
return null;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new Exception("The method or operation is not implemented.");
}
}
可以按如下方式使用
<Rectangle Fill="{Binding Path=MyColorProperty,
Converter={StaticResource ColorToBrushConverter}} "/)
如果您在 Google 上搜索 ColorToBrushConverter,您会发现有很多人实现了这个简单的小转换器。 但是,如果您想绑定到 color
的 string
表示形式怎么办? 或者您想绑定到笔触虚线属性或路径几何? 虽然值转换器实现起来很简单,但您必须创建这么多转换器实在可惜!
通用值转换器
如果有一个与 XAML 解析器具有相同灵活性的单个值转换器,那不是很好吗? 实际上,创建这样的转换器非常简单(并且在创建了我的第 5 个 ColorToBrushConverter
之后,我不知道为什么这么久才意识到这一点!)。 .NET Framework 通过 TypeConverters 长时间拥有一个用于在不同类型之间进行转换的 API。 它们广泛用于 .NET 技术中的数据绑定和设计器支持等。
值转换器可以为目标属性获取合适的 TypeConverter
,然后执行所需的转换
public class UniversalValueConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
// obtain the converter for the target type
TypeConverter converter = TypeDescriptor.GetConverter(targetType);
try
{
// determine if the supplied value is of a suitable type
if (converter.CanConvertFrom(value.GetType()))
{
// return the converted value
return converter.ConvertFrom(value);
}
else
{
// try to convert from the string representation
return converter.ConvertFrom(value.ToString());
}
}
catch (Exception)
{
return value;
}
}
public object ConvertBack
(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
请注意,此值转换器首先尝试直接从源类型转换为目标类型,如果不可能,则尝试通过 string
表示形式进行转换(我不确定为 String
类型获取 TypeConverter
,然后使用它而不是在要转换的值上调用 ToString
是否更正确,但以上方法对我有用 :) )。
您可以在下面的示例中看到此转换器的实际效果,其中演示了一系列类型转换
<TextBlock Text="Converting String to Brush ...."/>
<Rectangle Fill="{Binding ElementName=colorTextBox, Path=Text,
Converter={StaticResource UniversalValueConverter}}"/>
<TextBox x:Name="colorTextBox"
Text="Red"/>
<TextBlock Text="Converting String to Geometry ...."/>
<Path Data="{Binding ElementName=geometryText, Path=Text,
Converter={StaticResource UniversalValueConverter}}"/>
<TextBox x:Name="geometryText"
Text="M 10,20 C 10,2.5 40,35 40,17 H 28"/>
<TextBlock Text="Converting String to DoubleCollection (stroke dash) ...."/>
<Line StrokeDashArray="{Binding ElementName=dashText, Path=Text,
Converter={StaticResource UniversalValueConverter}}"/>
<TextBox x:Name="dashText"
Text="2 2 4 5"/>
对于第一次转换,string
到画笔,您可以使用命名颜色以及十六进制表示法及其缩写形式(#AF7、#AAFF77、#FFAAFF77 …)。 您还可以使用此转换器将 string
转换为相应的 enum
值,例如将 string
“Collapsed
” 绑定到 Visibility
属性。
代码隐藏中的值转换
上面的转换器确实是绑定的瑞士军刀,但是代码隐藏呢? 您仍然受到要设置的属性的类型要求的约束
rect1.Fill = new SolidColorBrush()
{
Color = Colors.Red
};
值转换器通常在绑定中使用,但也可以直接在代码隐藏中使用。 以下扩展方法扩展了 SetValue
方法,用于设置依赖属性以使用上面的值转换器
/// <summary>
/// Sets the given dependency property, applying type conversion where required
/// </summary>
public static void SetValueEx(this DependencyObject element,
DependencyProperty property, object value)
{
var conv = new UniversalValueConverter();
var convertedValue = conv.Convert(value, property.PropertyType,
null, CultureInfo.InvariantCulture);
element.SetValue(property, convertedValue);
}
它提供了一种更灵活的机制来设置属性值
rect1.SetValueEx(Rectangle.FillProperty, Colors.Red);
rect2.SetValueEx(Rectangle.FillProperty, "Blue");
rect3.SetValueEx(Rectangle.FillProperty, "#FFEE55");
rect4.SetValueEx(Rectangle.FillProperty, new SolidColorBrush(Colors.Orange));
…Silverlight 呢?
不幸的是,Silverlight 缺少用于获取 TypeConveters
的 TypeDescriptor
类。 我猜测 Silverlight 中的类型转换已“烘焙”到 XAML 解析器中,这意味着无法重用此逻辑。 :(
您可以从 此处 下载此博客文章的完整源代码。
此致,
Colin E.