Binding还有另外一种机制称为数据转换(Data Convert),当Source端Path所关联的数据与Target端目标属性数据类型不一致时,可以定义数据转换器(Data Converter)。当WPF不能自动转换数据类型时,只能自己写Converter,方法是创建一个类并让这个类实现IValueConverter接口。当数据从Binding的Source流向Target时,Convert方法将被调用;反之,ConvertBack方法将被调用。
Binding对象的Mode属性会影响到这两个方法的调用。如果Mode为TwoWay或Default行为与TwoWay一致则两个方法都可能被调用;如果Mode为OneWay或Default行为与OneWay一致则只有Convert方法会被调用。事例如下:
XAML:
<Window.Resources>
<local:CategoryToSourceConverter x:Key="cts"/>
<local:StateToNullablBoolConverter x:Key="stnb"/>
</Window.Resources>
<StackPanel Background="LightBlue">
<ListBox x:Name="listBoxPlane" Height="160" Margin="5">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Width="20" Height="20" Source="{Binding Path=Category, Converter={StaticResource ResourceKey=cts}}"/>
<TextBlock Text="{Binding Path=Name}" Width="60" Margin="80, 0"/>
<CheckBox IsThreeState="True" IsChecked="{Binding Path=State, Converter={StaticResource ResourceKey=stnb}}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button x:Name="buttonLoad" Content="Load" Height="25" Margin="5, 0" Click="buttonLoad_Click"/>
<Button x:Name="buttonSave" Content="Save" Height="25" Margin="5, 5" Click="buttonSave_Click"/>
</StackPanel>
C#代码:
public enum Category
{
Bomber,
Fighter
}
public enum State
{
Available,
Locked,
Unknown
}
public class Plane
{
public Category Category { get; set; }
public string Name { get; set; }
public State State { get; set; }
}
public class CategoryToSourceConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
Category c = (Category)value;
switch (c)
{
case Category.Bomber:
return @"\Icons\Bomber.png";
case Category.Fighter:
return @"\Icons\Fighter.png";
default:
return null;
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class StateToNullablBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
State s = (State)value;
switch (s)
{
case State.Available:
return true;
case State.Locked:
return false;
case State.Unknown:
default:
return null;
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
bool? nb = (bool?)value;
switch (nb)
{
case true:
return State.Available;
case false:
return State.Locked;
case null:
default:
return State.Unknown;
}
}
}
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void buttonLoad_Click(object sender, RoutedEventArgs e)
{
List<Plane> planeList = new List<Plane>
{
new Plane{Category = Category.Bomber, Name = "B--1", State = State.Unknown},
new Plane{Category = Category.Bomber, Name = "B--2", State = State.Unknown},
new Plane{Category = Category.Fighter, Name = "F--22", State = State.Unknown},
new Plane{Category = Category.Fighter, Name = "Su--47", State = State.Unknown},
new Plane{Category = Category.Bomber, Name = "B--52", State = State.Unknown},
new Plane{Category = Category.Fighter, Name = "J--10", State = State.Unknown}
};
this.listBoxPlane.ItemsSource = planeList;
}
private void buttonSave_Click(object sender, RoutedEventArgs e)
{
StringBuilder sb = new StringBuilder();
foreach (Plane p in listBoxPlane.Items)
{
sb.AppendFormat(string.Format("Category = {0}, Name = {1}, State = {2}", p.Category, p.Name, p.State));
}
File.WriteAllText(@"D:\PlaneList.txt", sb.ToString());
}
}