值转换器经常与数据绑定一起使用。以下是一些基本示例:
- 您有一个数值,但您想以一种方式显示零值并以另一种方式显示正数
- 您想根据值检查 CheckBox,但该值是类似“yes”或“no”的字符串,而不是布尔值
- 您有一个以字节为单位的文件大小,但您希望根据它的大小将其显示为字节、千字节、兆字节或千兆字节
这些是一些简单的情况,但还有更多。例如,您可能希望根据布尔值检查复选框,但您希望将其反转,以便在值为 false 时检查 CheckBox,而在值为 true 时不检查。您甚至可以使用转换器根据值为 ImageSource 生成图像,例如绿色符号表示真或红色符号表示假 - 可能性几乎是无限的!
对于这种情况,您可以使用值转换器。这些实现 IValueConverter 接口的小类将充当中间人并在源和目标之间转换值。因此,在任何需要在值到达目的地或再次返回其源之前转换值的情况下,您可能都需要一个转换器。
实现一个简单的值转换器
如前所述,WPF 值转换器需要实现 IValueConverter 接口,或者 IMultiValueConverter 接口(稍后会详细介绍)。这两个接口都只需要您实现两个方法:Convert() 和 ConvertBack()。顾名思义,这些方法将用于将值转换为目标格式,然后再返回。
让我们实现一个简单的转换器,它接受一个字符串作为输入,然后返回一个布尔值,反之亦然。如果您是 WPF 的新手,并且很可能因为您正在阅读本教程,那么您可能不了解示例中使用的所有概念,但不要担心,它们将在代码清单后进行解释:
<Window x:Class="WpfTutorialSamples.DataBinding.ConverterSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfTutorialSamples.DataBinding"
Title="ConverterSample" Height="140" Width="250">
<Window.Resources>
<local:YesNoToBooleanConverter x:Key="YesNoToBooleanConverter" />
</Window.Resources>
<StackPanel Margin="10">
<TextBox Name="txtValue" />
<WrapPanel Margin="0,10">
<TextBlock Text="Current value is: " />
<TextBlock Text="{Binding ElementName=txtValue, Path=Text, Converter={StaticResource YesNoToBooleanConverter}}"></TextBlock>
</WrapPanel>
<CheckBox IsChecked="{Binding ElementName=txtValue, Path=Text, Converter={StaticResource YesNoToBooleanConverter}}" Content="Yes" />
</StackPanel>
</Window>
using System;
using System.Windows;
using System.Windows.Data;
namespace WpfTutorialSamples.DataBinding
{
public partial class ConverterSample : Window
{
public ConverterSample()
{
InitializeComponent();
}
}
public class YesNoToBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
switch(value.ToString().ToLower())
{
case "yes":
case "oui":
return true;
case "no":
case "non":
return false;
}
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if(value is bool)
{
if((bool)value == true)
return "yes";
else
return "no";
}
return "no";
}
}
}
代码隐藏
因此,让我们从后面开始,然后通过示例进行操作。我们在代码隐藏文件中实现了一个名为 YesNoToBooleanConverter 的转换器。正如宣传的那样,它只实现了两个必需的方法,称为 Convert() 和 ConvertBack()。Convert() 方法假定它接收一个字符串作为输入(value参数),然后将其转换为布尔型 true 或 false 值,后备值为 false。为了好玩,我还添加了从法语单词进行这种转换的可能性。
ConvertBack() 方法显然做了相反的事情:它假设输入值为布尔类型,然后返回英文单词“yes”或“no”,回退值为“no”。
您可能想知道这两种方法采用的附加参数,但在本示例中不需要它们。我们将在下一章中使用它们,并在其中解释它们。
XAML
在程序的 XAML 部分,我们首先将转换器的实例声明为窗口的资源。然后我们有一个 TextBox、几个 TextBlock 和一个 CheckBox 控件,这就是有趣的事情发生的地方:我们将 TextBox 的值绑定到 TextBlock 和 CheckBox 控件,并使用 Converter 属性和我们自己的转换器引用,我们根据需要在字符串和布尔值之间来回切换值。
如果您尝试运行此示例,您将能够在两个位置更改值:通过在 TextBox 中写入“yes”(或任何其他值,如果您想要 false)或通过选中 CheckBox。无论您做什么,更改都会反映在其他控件以及 TextBlock 中。
StringFormat 属性
正如我们在前几章中看到的,在显示之前操作绑定输出的方法通常是通过使用转换器。转换器很酷的一点是,它们允许您将任何数据类型转换为完全不同的数据类型。但是,对于更简单的使用场景,您只想更改某个值的显示方式,而不必将其转换为其他类型,StringFormat属性可能就足够了。
使用绑定的 StringFormat 属性,您会失去使用转换器时获得的一些灵活性,但作为回报,它使用起来要简单得多,并且不涉及在新文件中创建新类。
StringFormat 属性正如其名称所暗示的那样:它格式化输出字符串,只需调用 String.Format 方法。有时一个例子会说一千多个字,所以在我达到这个字数之前,让我们直接进入一个例子:
<Window x:Class="WpfTutorialSamples.DataBinding.StringFormatSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib"
Title="StringFormatSample" Height="150" Width="250"
Name="wnd">
<StackPanel Margin="10">
<TextBlock Text="{Binding ElementName=wnd, Path=ActualWidth, StringFormat=Window width: {0:#,#.0}}" />
<TextBlock Text="{Binding ElementName=wnd, Path=ActualHeight, StringFormat=Window height: {0:C}}" />
<TextBlock Text="{Binding Source={x:Static system:DateTime.Now}, StringFormat=Date: {0:dddd, MMMM dd}}" />
<TextBlock Text="{Binding Source={x:Static system:DateTime.Now}, StringFormat=Time: {0:HH:mm}}" />
</StackPanel>
</Window>
前两个 TextBlock 通过绑定到父 Window 并获取其宽度和高度来获取它们的值。通过 StringFormat 属性,值被格式化。对于宽度,我们指定一个自定义格式字符串,对于高度,我们要求它使用货币格式,只是为了好玩。该值保存为 double 类型,因此我们可以使用所有相同的格式说明符,就像我们调用 double.ToString() 一样。
还要注意我如何在 StringFormat 中包含自定义文本 - 这允许您根据需要使用文本预先/后修复绑定值。当引用格式字符串中的实际值时,我们用一组大括号将其括起来,其中包括两个值:对我们要格式化的值的引用(值编号 0,这是第一个可能的值)和格式字符串, 以冒号分隔。
对于最后两个值,我们简单地绑定到当前日期 (DateTime.Now) 并首先以特定格式将其作为日期输出,然后作为时间(小时和分钟),再次使用我们自己的预定义的格式。
没有额外文本的格式
请注意,如果您指定一个不包含任何自定义文本的格式字符串(上面的所有示例都这样做),那么在 XAML 中定义它时,您需要添加一组额外的花括号。原因是 WPF 否则可能会将语法与用于标记扩展的语法混淆。下面是一个例子:
<Window x:Class="WpfTutorialSamples.DataBinding.StringFormatSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib"
Title="StringFormatSample" Height="150" Width="250"
Name="wnd">
<WrapPanel Margin="10">
<TextBlock Text="Width: " />
<TextBlock Text="{Binding ElementName=wnd, Path=ActualWidth, StringFormat={}{0:#,#.0}}" />
</WrapPanel>
</Window>
使用特定的文化
如果您需要根据特定文化输出绑定值,那没有问题。绑定将使用为父元素指定的语言,或者您可以使用 ConverterCulture 属性直接为绑定指定它。下面是一个例子:
<Window x:Class="WpfTutorialSamples.DataBinding.StringFormatCultureSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib"
Title="StringFormatCultureSample" Height="120" Width="300">
<StackPanel Margin="10">
<TextBlock Text="{Binding Source={x:Static system:DateTime.Now}, ConverterCulture='de-DE', StringFormat=German date: {0:D}}" />
<TextBlock Text="{Binding Source={x:Static system:DateTime.Now}, ConverterCulture='en-US', StringFormat=American date: {0:D}}" />
<TextBlock Text="{Binding Source={x:Static system:DateTime.Now}, ConverterCulture='ja-JP', StringFormat=Japanese date: {0:D}}" />
</StackPanel>
</Window>