在基本绑定中,信息从源到目标的传递过程中没有任何变化。这看起来是符合逻辑的,但我们并不总是希望出现这种行为。通常,数据源使用的是低级表达方式,我们可能不希望直接在用户界面使用这种低级表达方式。WPF提供了两个工具,来进行数据转换:
字符串格式化
通过设置 Binding.StringFormat 属性对文本形式的数据进行转换——例如包含日期和数字的字符串。
值转换器
该功能更强大,使用该功能可以将任意类型的源数据转换为任意类型的对象表示,然后可以传递到关联的控件。
使用StringFormat属性
可以看到后面两个StringFormat属性以花括号 {} 开头,完整值是 {}{0:C},而不是 {0:C},第一个则只有 {0:C},这是因为在StringFormat 值以花括号开头时需要 {} 转义序列。
使用值转换器
为创建子转换器需要执行以下四个步骤:
1、创建一个实现IValueConverter接口的类
2、为该类声明添加ValueConversion特性,并指定目标数据类型
3、实现Convert()方法,该方法将数据从原来的格式转换为显示的格式
4、实现ConvertBack()方法,该方法执行反向变换,将值从显示格式转换为原格式
[ValueConversion(typeof(decimal), typeof(string))]
public class PriceConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
decimal price = (decimal)value;
return price.ToString("C", culture);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
string price = (string)value;
if (decimal.TryParse(price, System.Globalization.NumberStyles.Any, culture, out decimal result))
{
return result;
}
return value;
}
}
要使用这个转换器,可以将其添加到页面的资源下,然后使用 Binding.Converter 指定。
多重绑定
可以将多个字段绑定到同一个输出控件,可以通过 StringFormat 或 MultiBinding.Converter 来格式化数据。多重绑定的值转换器需要实现的接口是 IMultiValueConverter,与 IValueConverter 接口比较类似,只是转换函数的第一个参数改成了数组形式。
public class MultiValueConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
decimal price = (decimal)values[0];
int volume = (int)values[1];
return (price * volume).ToString("C");
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
}
完整代码如下:
MainWindow.xaml
ConvertWithConverter:
ConvertDateTime:
ConvertImagePath:
MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace TestDataConverter;
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected virtual bool SetProperty(ref T member, T value, [CallerMemberName] string? propertyName = null)
{
if (EqualityComparer.Default.Equals(member, value))
{
return false;
}
member = value;
OnPropertyChanged(propertyName);
return true;
}
}
public class Order : ViewModelBase
{
public decimal price = 0;
public decimal Price { get => price; set => SetProperty(ref price, value); }
public int volume = 0;
public int Volume { get => volume; set => SetProperty(ref volume, value); }
public DateTime orderDate = DateTime.Now;
public DateTime OrderDate { get => orderDate; set => SetProperty(ref orderDate, value); }
public string image = string.Empty;
public string Image { get => image; set => SetProperty(ref image, value); }
}
[ValueConversion(typeof(decimal), typeof(string))]
public class PriceConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
decimal price = (decimal)value;
return price.ToString("C", culture);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
string price = (string)value;
if (decimal.TryParse(price, System.Globalization.NumberStyles.Any, culture, out decimal result))
{
return result;
}
return value;
}
}
[ValueConversion(typeof(decimal), typeof(Brush))]
public class PriceToBackgroundConverter : IValueConverter
{
public decimal MinimumPriceToHighlight { get; set; }
public Brush HighlightBrush { get; set; }
public Brush DefaultBrush { get; set; }
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
decimal price = (decimal)value;
if (price >= MinimumPriceToHighlight)
return HighlightBrush;
else
return DefaultBrush;
}
public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
}
public class ImagePathConverter : IValueConverter
{
private string imageDirectory = Directory.GetCurrentDirectory();
public string ImageDirectory
{
get { return imageDirectory; }
set { imageDirectory = value; }
}
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string imagePath = Path.Combine(ImageDirectory, (string)value);
return new BitmapImage(new Uri(imagePath));
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException("The method or operation is not implemented.");
}
}
public class MultiValueConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
decimal price = (decimal)values[0];
int volume = (int)values[1];
return (price * volume).ToString("C");
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
myGrid.DataContext = Order;
Order.Price = 100;
Order.Volume = 10;
Order.OrderDate = DateTime.Now;
Order.Image = "image1.gif";
}
public Order Order = new Order();
private void Button_Click(object sender, RoutedEventArgs e)
{
Console.WriteLine("");
}
}