Binding的数据转换

本文介绍了WPF Binding中的数据转换机制,通过创建实现IValueConverter接口的转换器类来处理不同数据类型的映射。举例说明了如何在列表中显示军用分级状态,涉及Category到string和State到bool?的双向转换。

Binding有一种机制成为数据转换(Data Convert),当Source端Path所关联数据与Target端目标属性数据类型不一致时,我们可以添加数据转换器(Data Converter)。

手写Converter的方式是创建一个类并让这个类实现IValueConverter接口。IValueConverter接口定义如下:

    public interface IValueConverter
    {
        object Convert(object value, Type targetType, object parameter, CultureInfo culture);
        
        object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture);
    }

当数据从Binding的Source流向Target时,Convert方法将被调用;反之,当Binding的Target流向Source时,ConverterBack将被调用。这两个方法的参数列表一模一样:第一个参数是object,最大限度地保证了Converter的重要性(可以在方法体内对实际类型进行判断);第二个参数用于确定方法的返回类型(个人认为形参名字叫outputType比targetType要好,可以避免与Binding的Target混淆);第三个参数用于把额外的信息传入方法,若需要传递多个信息则可以把信息放入一个集合对象来传入方法。

Binding对象的Mode属性会影响到这两个方法的调用。如果Mode为TwoWay或Default行为与TwoWay一致则两个方法都可能被调用;如果Mode为OneWay或Default行为与OneWay一致则可能只有Convert方法会被调用;其他情况同理。

下面的例子是一个Converter的综合实例,程序的用途是在列表中向玩家显示一些军用分级的状态。

首先创建几个自定义数据类型:

    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; }
    }

在UI里Plane的Category属性呗映射为轰炸机或者战斗机的图标,把这两个图标加入到该项目中。

同时飞机的State属性在UI里被映射为CheckBox。因为存在以上两个映射关系,我们需要提供两个Converter:一个是由Category类型单向转换为string类型(XAML编译器能够把string对象解析成图片资源URI),另一个是在State与bool?类型之间双向转换。

    public class CategoryToSourceConverter:IValueConverter
    {
        //将Category转换为Uri图片资源
        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.jpg";
                case Category.Fighter:
                    return @"\Icons\Plane.jpg";
                default:
                    return null;
            }
        }

        //不会被调用
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }

    public class StateToNullableBoolConverter:IValueConverter
    {
        //将State转换为bool?
        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;
            }
        }
        //将bool?转换为State   bool? 可以为 null 的类型可以包含三个不同的值:true、false 和 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;
            }
        }
    }

下面我们如何在XAML里消费这些Converter呢?

<Window x:Class="Chapter6.Page124.ConverterDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:Chapter6.Page124.ConverterDemo"
        Title="MainWindow" Height="350" Width="525">
    <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">
                        <Image Width="20" Height="20" Source="{Binding Path=Category,Converter={StaticResource cts}}"/>
                        <TextBlock Text="{Binding Path=Name}" Width="60" Margin="80,0"/>
                        <CheckBox IsThreeState="True" IsChecked="{Binding Path=State,Converter={StaticResource 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>
</Window>
XAML代码中已经添加了对程序集的引用并映射为名称为Local,同时,以资源的形式创建了两个Converter实例。名为listBoxPlane和ListBox控件是我们的重点,需要为他们添加用于显示数据的DataTemplate。
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void buttonLoad_Click(object sender, RoutedEventArgs e)
        {
            List<Plane> planList = 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},
            };
            this.listBoxPlane.ItemsSource = planList;
        }

        private void buttonSave_Click(object sender, RoutedEventArgs e)
        {
            StringBuilder sb = new StringBuilder();
            foreach (Plane p in listBoxPlane.Items)
            {
                sb.AppendLine(string.Format("Category={0}, Name={1}, State={2}", p.Category, p.Name, p.State));
            }
            File.WriteAllText(@"D:\PlaneList.txt", sb.ToString());
        }
    }


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值