65.9K
CodeProject 正在变化。 阅读更多。
Home

WPF 通用值转换器

starIconstarIconstarIconstarIconstarIcon

5.00/5 (13投票s)

2010 年 7 月 9 日

CPOL

3分钟阅读

viewsIcon

39072

一个简单的 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,您会发现有很多人实现了这个简单的小转换器。 但是,如果您想绑定到 colorstring 表示形式怎么办? 或者您想绑定到笔触虚线属性或路径几何? 虽然值转换器实现起来很简单,但您必须创建这么多转换器实在可惜!

通用值转换器

如果有一个与 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 值,例如将 stringCollapsed” 绑定到 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 缺少用于获取 TypeConvetersTypeDescriptor 类。 我猜测 Silverlight 中的类型转换已“烘焙”到 XAML 解析器中,这意味着无法重用此逻辑。 :(

您可以从 此处 下载此博客文章的完整源代码。

此致,
Colin E.

© . All rights reserved.