前面我们讲解的数据绑定都是非常简单的数据类型,基本上都是int或者string,都可以很容易地显示在界面上。现在想象一个这样的场景:我们定义了一个枚举,这个枚举有2个枚举值,checked和unchecked。界面上有一个checkbox,当枚举值为checked时,复选框被选中。因为checkbox是无法识别枚举值的,所以这里需要一个数据的转换,把枚举值转换成bool值。
下面我们看一个例子:
xaml代码:
<Window x:Class="BindValidationRule.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:BindValidationRule"
mc:Ignorable="d"
Title="Window1" Height="450" Width="800">
<Window.Resources>
<local:CategoryToSourceConverter x:Key="cts"/>
<local:StateToNullableBoolConverter x:Key="stnb"/>
</Window.Resources>
<StackPanel>
<ListBox x:Name="listBoxPlane" Height="160" Margin="5">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Width="60" Text="{Binding Path=Category, Converter={StaticResource cts}}"/>
<TextBlock Text="{Binding Path=Name}" Width="60"/>
<CheckBox IsThreeState="True" IsChecked="{Binding Path=State,Converter={StaticResource stnb}}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button x:Name="btnLoad" Content="Load" Height="30" Click="btnLoad_Click"/>
</StackPanel>
</Window>
c#代码:
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
private void btnLoad_Click(object sender, RoutedEventArgs e)
{
List<Plane> planes = new List<Plane>()
{
new Plane(){Category= Category.Bomber,Name="B-1",State= State.Unknown},
new Plane(){Category= Category.Bomber,Name="B-2",State= State.Avalible},
new Plane(){Category= Category.Fighter,Name="B-3",State= State.Locked},
};
listBoxPlane.ItemsSource = planes;
}
}
public enum Category
{
Bomber,
Fighter
}
public enum State
{
Avalible,
Locked,
Unknown
}
public class Plane
{
public Category Category { get; set; }
public State State { get; set; }
public string Name { get; set; }
}
public class CategoryToSourceConverter:IValueConverter
{
public object Convert(object value,Type targetType,object parameter,CultureInfo cultureInfo)
{
Category category = (Category)value;
switch(category)
{
case Category.Bomber:
return "A";
case Category.Fighter:
return "B";
default:
return null;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo cultureInfo)
{
throw new NotImplementedException();
}
}
public class StateToNullableBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo cultureInfo)
{
State state = (State)value;
switch (state)
{
case State.Avalible:
return true;
case State.Locked:
return false;
default:
return null;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo cultureInfo)
{
bool? nb = (bool?)value;
switch(nb)
{
case true:
return State.Avalible;
case false:
return State.Locked;
default:
return State.Unknown;
}
}
}
当我们点击load按钮时,界面显示效果如下:
我们这里给第一列的TextBlock的Text属性指定了binding,path=Category,而我们定义了Category是一个枚举。所以我们也给这个binding指定了一个Converter。这个转换器定义在Window.Resources里面。下面我们重点看一下它是如何实现转换的:
我们看CategoryToSourceConverter类,它继承自IValueConverter接口,这个接口的定义是这样的:
它只有2个函数,Convert是把源数据转换成目标数据,ConvertBack则相反。在我们这个例子里面,源指的就是Category这个枚举,目标数据指的就是TextBlock的Text属性值。
第一个参数value是源的值,第二个参数用于确定方法的返回类型;第三个参数用于把额外的信息传入方法,若需要传递多个信息则可以把信息放入一个集合对象来传入方法;第四个参数用于提供有关特定区域性的信息。
在这个例子里面,因为我们知道value的值是Category,所以直接把value强制转换成Category。然后根据不同的枚举值返回相应的数据,我做的例子是为了方便随便写的字符串。
前台设置转换器,后台实现转换逻辑,这样一个完整的转换器就做好了。我们这里暂时不考虑把目标数据转换到源数据,所以并没有实现ConvertBack方法。
下面checkbox的转换思想和上面TextBlock是一样的,这里就不再赘述了。
但是这里有个地方要提一下,我们看到checkbox有一个属性IsThreeState=true。checkbox默认是2种状态,选中或者没选中,但是它还有一种状态就是半选中。当IsChecked=null时,它就是半选中状态。