ivalueconverter是数据绑定中的强大工具,用于在binding source和binding target之间转换值。本文将详细介绍ivalueconverter的使用方法及一些常见的实现。
假设有一个名为TestResult的类:
public class TestResult
{
public bool Passed { get; set; }
}UI需要根据Passed属性的值来决定结果文本的颜色是红色还是绿色。初学者通常会通过修改TestResult类,添加一个与Passed相关的属性来实现:
public class TestResult
{
public bool Passed { get; set; }
public Brush TestResultBrush
{
get
{
if (Passed)
return new SolidColorBrush(Colors.Red);
else
return new SolidColorBrush(Colors.Green);
}
}
}然后在XAML中绑定到这个属性:
<textblock foreground="{Binding TestResultBrush}" text="Score : 60"></textblock>另一种方法是直接在Code Behind中为TextBlock更改Foreground:
var testResult = DataContext as TestResult;
if (testResult != null)
{
if (testResult.Passed)
ResultElement.Foreground = new SolidColorBrush(Colors.Red);
else
ResultElement.Foreground = new SolidColorBrush(Colors.Green);
}这两种方法都不够优雅,会带来一系列问题:破坏了TestResult的结构,违反了开放封闭原则,导致UI和数据过度耦合,存在过多的硬编码等。
在这种情况下,通常可以使用IValueConverter来处理。在Binding中,IValueConverter可以将数据在呈现前转换为新的目标值。实现IValueConverter需要执行以下步骤:
public object Convert(object value, Type targetType, object parameter, string language)
public object ConvertBack(object value, Type targetType, object parameter, string language)
在这个例子中,IValueConverter的目的是将bool类型的Passed转换为Brush,实现如下:
public class BoolToBrushConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is bool passed)
return new SolidColorBrush(passed ? Colors.Green : Colors.Red);
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}在XAML中使用这个Converter需要先将其定义为Resource,然后在Binding中指定Converter到这个已定义的Resource:
<grid background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<grid.resources>
<booltobrushconverter x:key="BoolToBrushConverter"></booltobrushconverter>
</grid.resources>
<textblock foreground="{Binding Passed,Converter={StaticResource BoolToBrushConverter}}" text="Score : 60"></textblock>
</grid>在XAML的漫长历史中,IValueConverter衍生出了一些奇妙的技巧,其中最常用的是BoolToValueConverter。
public class BoolToValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value == null || (bool) value == false)
return DependencyProperty.UnsetValue;
return parameter;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
return Equals(value, parameter);
}
}BoolToValueConverter灵活使用了Binding中的ConverterParameter和FallbackValue两个参数,常用于解决IValueConverter中硬编码的问题。在Binding中,FallbackValue指明了如果Binding无法返回任何值时使用的值,在IValueConverter中返回DependencyProperty.UnsetValue即告诉Binding要使用FallbackValue的值。
使用BoolToValueConverter解决了上述例子的硬编码问题,在XAML中使用如下:
<grid>
<grid.resources>
<booltovalueconverter x:key="BoolToValueConverter"></booltovalueconverter>
</grid.resources>
<textblock foreground="{Binding Passed,Converter={StaticResource BoolToValueConverter},ConverterParameter=Green,FallbackValue=Red}" text="Score : 60"></textblock>
</grid>需要注意的是,上述XAML中的Green和Red只是字符串,它们最终能被解析成SolidColorBrush是由于TypeConverter的支持。也就是说,上述XAML语法只能用于TypeConverter支持的数据类型,而且这种写法仍然过于硬编码。如果要支持复杂类型或本地化等问题,可以将ConverterParameter和FallbackValue绑定到StaticResource:
<grid.resources>
<solidcolorbrush color="Green" x:key="PassedBrush"></solidcolorbrush>
<solidcolorbrush color="Red" x:key="FailedBrush"></solidcolorbrush>
<booltovalueconverter x:key="BoolToValueConverter"></booltovalueconverter>
</grid.resources>
<textblock foreground="{Binding Passed,Converter={StaticResource BoolToValueConverter},ConverterParameter={StaticResource PassedBrush},FallbackValue={StaticResource FailedBrush}}" text="Score : 60"></textblock>尽管这种方法看似很灵活,但如果有大量返回相同值的BoolToValueConverter,将会使XAML产生大量冗余。UWP Community Toolkit提供了一些常用的IValueConverter实现,其中最常用的是BoolToObjectConverter。BoolToObjectConverter和BoolToValueConverter功能类似,但它提供了
public object TrueValue { get; set; }public object FalseValue { get; set; }<grid.resources>
<solidcolorbrush color="Green" x:key="PassedBrush"></solidcolorbrush>
<solidcolorbrush color="Red" x:key="FailedBrush"></solidcolorbrush>
<booltoobjectconverter falsevalue="{StaticResource FailedBrush}" truevalue="{StaticResource PassedBrush}" x:key="BoolToObjectConverter"></booltoobjectconverter>
</grid.resources>
<textblock foreground="{Binding Passed,Converter={StaticResource BoolToObjectConverter}}" text="Score : 60"></textblock>UWP Community Toolkit中提供了另一个常用的Converter:BoolToVisibilityConverter。这个Converter只是简单地继承了BoolToObjectConverter,并且为TrueValue和FalseValue设置了默认值:
public BoolToVisibilityConverter()
{
TrueValue = Visibility.Visible;
FalseValue = Visibility.Collapsed;
}BoolToVisibilityConverter虽然简单,但确实好用。不过,从1607版本之后就不需要这个Converter了,微软是这样说的。
但有时候反而需要True对应Collapsed,于是现在是另一个常用Converter - BoolNegationConverter登上历史舞台的时候了:
<stackpanel>
<stackpanel.resources>
<boolnegationconverter x:key="BoolNegationConverter"></boolnegationconverter>
</stackpanel.resources>
<textblock foreground="Green" text="Passed" visibility="{Binding Passed}"></textblock>
<textblock foreground="Red" text="Failed" visibility="{Binding Passed,Converter={StaticResource BoolNegationConverter}}"></textblock>
</stackpanel>UWP的Binding缺少了StringFormat,这对Binding产生了很大影响。为了弥补这个缺陷,可以使用UWP Community Toolkit中的StringFormatConverter。它的代码也十分简单(其实这才是ConverterParameter的正确用法):
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value == null)
{
return null;
}
if (parameter == null)
{
return value;
}
return string.Format((string)parameter, value);
}在XAML中使用如下:
<textblock text="{Binding DoubleValue,Converter={StaticResource StringFormatConverter},ConverterParameter='{}{0:N2}'}"></textblock>
<textblock text="{Binding DoubleValue,Converter={StaticResource StringFormatConverter},ConverterParameter='There are {0:N0} Items'}"></textblock>结果如下:
![[UWP]了解IValueConverter](https://img.php.cn/upload/article/001/503/042/175712126013835.jpg)
除了弥补StringFormat的功能,StringFormatConverter还有其他应用场景。
TestModel.CS
public IEnumerable<clickmode> ClickModes => new List<clickmode> { ClickMode.Hover, ClickMode.Press, ClickMode.Release };<listbox itemssource="{Binding ClickModes}">
<listbox.itemtemplate>
<datatemplate>
<textblock text="{Binding }"></textblock>
</datatemplate>
</listbox.itemtemplate>
</listbox>
<listbox itemssource="{Binding ClickModes}"></listbox>在WPF中,以上XAML都可以正常呈现,而在UWP中,以上XAML显示如下:
![[UWP]了解IValueConverter](https://img.php.cn/upload/article/001/503/042/175712126137274.jpg)
这种情况可以使用StringFormatConverter显示枚举的名称:
<listbox itemssource="{Binding ClickModes}">
<listbox.itemtemplate>
<datatemplate>
<textblock text="{Binding Converter={StaticResource StringFormatConverter}}"></textblock>
</datatemplate>
</listbox.itemtemplate>
</listbox>![[UWP]了解IValueConverter](https://img.php.cn/upload/article/001/503/042/175712126137033.jpg)
可以说对UWP来说,StringFormatConverter十分必要。
public object Convert(object value, Type targetType, object parameter, string language)
public class DateTimeValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is DateTime dateTime)
{
var culture = new CultureInfo(language);
return dateTime.ToString(culture.DateTimeFormat);
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}<textblock text="{Binding Date,Converter={StaticResource DateTimeValueConverter},ConverterLanguage=en-US}"></textblock>
<textblock text="{Binding Date,Converter={StaticResource DateTimeValueConverter},ConverterLanguage=zh-CN}"></textblock>结果如下:
![[UWP]了解IValueConverter](https://img.php.cn/upload/article/001/503/042/175712126117462.jpg)
targetType参数指转换后的目标类型,使用这个参数可以实现一个简单的Value Converter:
public class ValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
return System.Convert.ChangeType(value, targetType);
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}虽然代码简单,但它可以解决不少问题,例如《了解TypeConverter》这篇文章里提到的不能在XAML中使用decimal的问题。IValueConverter要起作用依赖于BindingSource,而在XAML中虽然很多东西都可以用来做BindingSource,例如用元素自己的Tag:
<mycontentcontrol amount="{Binding Converter={StaticResource ValueConverter},Path=Tag,RelativeSource={RelativeSource Mode=Self}}" tag="10.01"></mycontentcontrol>或者Resources中的字符串:
<grid.resources>
<string x:key="DecimalString">10.01</string>
</grid.resources>
<mycontentcontrol amount="{Binding Source={StaticResource DecimalString},Converter={StaticResource ValueConverter}}"></mycontentcontrol>或者更进一步写一个字符串的包装类:
public class StringWrapper
{
public string this[string key]
{
get
{
return key;
}
}
}<mycontentcontrol amount="{Binding [10.01],Source={StaticResource StringWrapper},Converter={StaticResource ValueConverter}}"></mycontentcontrol>9.1 统一管理IValueConverter
由于大部分IValueConverter行为是固定的,通常我都会把常用的IValueConverter放到一个Converters.xaml,然后在App.xaml中合并资源字典,这样不用重复写创建Converter的xaml,也避免了重复创建Converter的资源消耗:
<application.resources>
<resourcedictionary>
<resourcedictionary.mergeddictionaries>
<resourcedictionary source="Converters.xaml"></resourcedictionary>
</resourcedictionary.mergeddictionaries>
</resourcedictionary>
</application.resources>9.2 格式化
![[UWP]了解IValueConverter](https://img.php.cn/upload/article/001/503/042/175712126144589.jpg)
Binding最让人诟病的缺点就是它的语法太长,例如以上两个TextBlock,在IDE中很难判断它们有什么不同。很多时候我都会把XAML的格式化设置成“将每个属性分行放置”,如下图:
![[UWP]了解IValueConverter](https://img.php.cn/upload/article/001/503/042/175712126163914.jpg)
这样上面两个TextBlock的XAML就清晰许多了:
![[UWP]了解IValueConverter](https://img.php.cn/upload/article/001/503/042/175712126196931.jpg)
不过这样设置也并不全是好处,怎么设置具体还是看个人喜好和屏幕尺寸。
虽然IValueConverter的文章已经不少了,但还是常常见到乱来的IValueConverter实现,而且UWP的IValueConverter有一些改变,所以还是写了这篇文章。
我很想写一些常用的,或者容易用错的基础知识,但连IValueConverter都不知不觉就写得这么长了,实在没勇气写Binding的概念,何况关于Binding已经有很多很实用的文章。
我十分清楚文章写得太长就会被“保存到Pocket”,我也想每篇文章都能在三五分钟内看完,但偏偏越基础的概念就越能写得长,而且写得简短些又会被移出博客园首页,很难把握尺度。
下一篇文章会尽量写短一些。
以上就是[UWP]了解IValueConverter的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号