WPF Data Binding之数据的转换和校验【四】

Binding的作用就是架在Source和Target之间的桥梁,数据可以在这座桥梁的帮助下来流通。就像现实社会中桥梁需要设置安检和关卡一样,Binding这座桥上也可以设置关卡对数据进行验证,不仅如此,如果Binding两端需要不同的数据类型的时候我们还可以为数据设置转换器。

Binding用于数据有效性校验的关卡是他的ValidationRules属性,用于数据类型转换的关卡是它的Convert属性。

1.1 Binding的数据校验

Binding的ValidationRules属性是Collection<ValidationRule>,从它的名称和数据类型我们可以得知可以为每个Binding设置多个数据校验条件,每一个条件是一个ValidationRule对象。ValidationRule是一个抽象类,在使用的时候我们需要创建它的派生类并实现它的Validate方法的返回值是ValidationResult类型对象,如果通过验证,就把ValidationResult对象的IsValidate属性设为true,反之,则需要将IsValidate设置为false并为其ErrorContent属性设置一个合适的消息内容(一般是字符串)。

下面这个程序的UI绘制一个TextBox和一个Slider,然后在后台C#代码中建立Binding把它们关联起来---- 以Slider为源,TextBox为目标。Slider的取值范围是0~100,也就是说我们需要验证TextBox中输入的值是不是在0~100之间。

XAML:

<span style="font-family:Microsoft YaHei;font-size:14px;"><Window x:Class="WpfApplication6.wnd641"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="wnd641" Height="200" Width="300">
    <StackPanel Background="LightSlateGray">
        <!--大小写敏感-->
        <!--Text="{Binding Path=Value, ElementName=_slider , UpdateSourceTrigger=PropertyChanged}"-->
        <TextBox x:Name="_txtBox" Margin="5" />
        <Slider x:Name="_slider" Minimum="0" Maximum="100" Margin="5" />
    </StackPanel>
</Window></span>

C# 数据校验继承类:

<span style="font-family:Microsoft YaHei;font-size:14px;">    // 数据校验
    public class RangValidationRule:ValidationRule
    {
        public override ValidationResult Validate(object value, CultureInfo cultureInfo)
        {
            double d = 0;
            if(double.TryParse(value.ToString(), out d))
            {
                if(d >=0 && d <= 100)
                {
                    return(new ValidationResult(true, null));
                }
            }
            return(new ValidationResult(false, "Validation Error"));
        }
    }
</span>

C#绑定:

<span style="font-family:Microsoft YaHei;font-size:14px;">    /// <summary>
    /// wnd641.xaml 的交互逻辑
    /// </summary>
    public partial class wnd641 : Window
    {
        public wnd641()
        {
            InitializeComponent();

            Binding binding = new Binding("Value")
            {
                Source = _slider,
                UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged,
            };
            RangValidationRule val = new RangValidationRule();

            // 目标更新时是否运行验证规则,默认来自Source的是不校验的
            val.ValidatesOnTargetUpdated = true;
            binding.ValidationRules.Add(val);
            binding.NotifyOnValidationError = true;
            _txtBox.SetBinding(TextBox.TextProperty, binding);

            // 添加错误提示的路由事件
            _txtBox.AddHandler(Validation.ErrorEvent, new RoutedEventHandler(ValidationError));
        }

        void ValidationError(object sender, RoutedEventArgs e)
        {
            if(Validation.GetErrors(_txtBox).Count > 0)
            {
                _txtBox.ToolTip = Validation.GetErrors(_txtBox)[0].ErrorContent.ToString();
            }
        }
    }
</span>

完成后运行程序,当输入0~100之间的值的时候程序正常显示,但是输入区间之外的值的时候TextBox会显示为红色边框,表示值是错误的,不能传值给Source。

1.2 Binding的数据转换

Binding还有另外一种机制称为数据转换,当Source端指定的Path属性值和Target端指定的目标属性不一致的时候,我们可以添加数据转换器(DataConvert)。上面我们提到的问题实际上就是double和stirng类型相互转换的问题,因为处理起来比较简单,所以WPF类库就自己帮我们做了,但有些数据类型转换就不是WPF能帮我们做的了,当遇到这些情况,我们只能自己动手写Converter,方法是创建一个类并让这个类实现IValueConverter接口。

当数据从Binding的Source流向Target的时候,Convert方法将被调用;反之ConvertBack将被调用。这两个方法的参数列表一模一样:第一个参数为Object。最大限度的保证了Convert的重要性。第二个参数用于确定返回参数的返回类型。第三个参数为了将额外的参数传入方法,若需要传递多个信息,则需要将信息做为一个集合传入即可。

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

下面这个例子是一个Converter的综合实例,程序的用途是向玩家显示一些军用飞机的状态信息。

C#:

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;

namespace WpfApplication6
{
    public enum Category
    {
        Bomber,
        Fighter
    }

    public class Plane
    {
        public Category Category { get; set; }
        public string Name { get; set; }
    }

    /// <summary>
    /// 数据转换
    /// </summary>
    public class CategoryToSourceCvt:IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            Category c = (Category)value;
            switch(c)
            {
                case Category.Bomber:
                    return (@"E:\RefCode\C#\WPF\深入浅出WPF\第六章Binding\WpfApplication6\WpfApplication6\Bomber.png");
                case Category.Fighter:
                    return (@"E:\RefCode\C#\WPF\深入浅出WPF\第六章Binding\WpfApplication6\WpfApplication6\Fighter.png");
            }
            return(null);

        }
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }

    }

    /// <summary>
    /// wnd642.xaml 的交互逻辑
    /// </summary>
    public partial class wnd642 : Window
    {
        public wnd642()
        {
            InitializeComponent();

            List<Plane> _listPanel = new List<Plane>()
            {
                new Plane(){Category = Category.Fighter, Name= "F-1"},
                new Plane(){Category = Category.Bomber, Name= "B-1"},
                new Plane(){Category = Category.Fighter, Name= "F-2"},
            };
            _listBox.ItemsSource = _listPanel;
        }
    }
}

XAML:

<Window x:Class="WpfApplication6.wnd642"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication6"
        Title="wnd642" Height="200" Width="300">
    <Window.Resources>
        <local:CategoryToSourceCvt x:Key="cts" />
    </Window.Resources>
    <StackPanel>
        <ListBox x:Name="_listBox" Height="160" Margin="5">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <!--Converter使用静态资源-->
                        <Image Source="{Binding Path=Category, Converter={StaticResource ResourceKey=cts}}" Width="20" Height="20"></Image>
                        <TextBlock Text="{Binding Path=Name}" Width="60" Height="20" Margin="80, 0"></TextBlock>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </StackPanel>
</Window>

1.3 MultiBinding(多路Binding)

有时候UI需要显示的数据来源不止一个数据来源决定,这个时候就需要用到MultiBinding,即多路绑定。MultiBinding与Binding一样均以BindingBase为基类,也就是说,凡是能用Binding的场合都能使用MultiBinding。MutiBinding具有一个Bindings的属性,其类型是Connection<BindingBase>,通过这个属性,MultiBinding把一组Binding对象聚合起来,处在这个Binding结合中的对象可以拥有自己的数据校验和转换机制。它们汇集起来的数据将共同决定传往MultiBinding目标的数据。如下图:

考虑这样一个需求,有一个用于新用户注册的UI(2个TextBox和一个Button),还有如下一些限定:

TextBox用于显示输入的邮箱,要求数据必须一致。

当TextBox的内容全部符合要求的时候,Button可用。

XAML:

<Window x:Class="WpfApplication6.wnd65"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="wnd65" Height="200" Width="300">
    <StackPanel>
        <TextBox x:Name="_txtBox1" Margin="5"></TextBox>
        <TextBox x:Name="_txtBox2" Margin="5"></TextBox>
        <Button Content="OK" x:Name="_btn" Margin="5"></Button>
    </StackPanel>
</Window>

C#:

    public class LogMulCvt:IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            // 元素强制转换为指定的string类型
            if(!values.Cast<string>().Any((text) => string.IsNullOrEmpty(text)) &&
                values[0].ToString() == values[1].ToString())
            {
                return (true);
            }
            return (false);
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

    /// <summary>
    /// wnd65.xaml 的交互逻辑
    /// </summary>
    public partial class wnd65 : Window
    {
        public wnd65()
        {
            InitializeComponent();

            Binding binding1 = new Binding("Text") { Source = _txtBox1 };
            Binding binding2 = new Binding("Text") { Source = _txtBox2 };
            MultiBinding mulBinding = new MultiBinding();
            mulBinding.Bindings.Add(binding1);
            mulBinding.Bindings.Add(binding2);
            mulBinding.Converter = new LogMulCvt();

            _btn.SetBinding(Button.IsEnabledProperty, mulBinding);
        }
    }

注意:

MultiBinding对子元素的顺序非常敏感,因为这个数据决定了汇集到Convert里数据的顺序。

MultiBinding的Converter实现的是IMultiValueConverter

WPF的核心理念是变传统的UI驱动数据变成数据驱动UI,支撑这个理念的基础就是本章讲的Data Binding和与之相关的数据校验和数据转换。在使用Binding的时候,最重要的就是设置它的源和路径。

参考《深入浅出WPF》

时间: 2024-10-23 18:12:31

WPF Data Binding之数据的转换和校验【四】的相关文章

WPF之Binding对数据的转换(第五天)

Binding在Slider控件与TextBox控件之间建立关联,值可以互相绑定,但是它们的数据类型是不同的,Slider是Double类型,Text为String.原来,Binding有一种机制称为数据转换(Data Converter),当数据绑定的源与目标不同类型时,处理比较简单时,系统就自动的进行了类型转换,但是对于相对复杂的类型转换时,就需要我们手动进行了. 下面用一个例子来说明Convert的应用,程序的用途是在列表里面向玩家显示一些球的状态. 首先创建几个自定义数据类型: publ

WPF Data Binding之地位与基础【一】

[1]Data Binding在WPF中的地位 从传统的Winform转移到WPF上,对于一个三层程序而言,数据存储层由数据库和文件系统组成,数据传输和处理仍然使用.NetFramework的ADO.NET等基本类(与Winform开发一样).展示层则使用WPF类库来实现,而展示层和逻辑层的沟通就使用Data Binding来实现.可见,Data Binding在WPF中所起的作用就是高速公路的作用.有了这条高速公路,加工好的数据自动送达用户界面并加以显示,被用户修改过的数据也会自动传回业务逻辑

WPF data binding

Binding这个类包含以下几个常用的属性: ElementName: Gets or sets the name of the elements to use as the binding source object. [Default is null] Source: Gets or sets the object to use as the binding source. RelativeSource: Gets or sets the binding source by specifyi

WPF Data Binding之控件作为源(Source)与路径(Path)【一】

Binding 的源也就是数据的源头.Binding对源的要求并不苛刻------只要它是一个对象,并且通过属性(Property)公开自己的数据,它就能作为Binding 的源. 前面一个例子已经向大家证明,如果想让作为Binding源的对象具有自动通知Binding自己属性值已经已经变化的能力,那么就需要让类实现INotifyChanged接口并在属性的Set语句中激发PropertyChanged事件.在日常生活中,除了使用这种对象作为数据源之外,我们还有更多的选择,比如用一个控件做为另一

JAVA对象JSON数据互相转换的四种常见情况

1. 把java 对象列表转换为json对象数组,并转为字符串 JSONArray array = JSONArray.fromObject(userlist);    String jsonstr = array.toString(); 2.把java对象转换成json对象,并转化为字符串 JSONObject object = JSONObject.fromObject(invite);   String str=object.toString()); 3.把JSON字符串转换为JAVA 对

【转载一篇WPF之Binding】WPF之Binding深入探讨

1,Data Binding在WPF中的地位 程序的本质是数据+算法.数据会在存储.逻辑和界面三层之间流通,所以站在数据的角度上来看,这三层都很重要.但算法在3层中的分布是不均匀的,对于一个3层结构的程序来说,算法一般分布在这几处: A.数据库内部. B.读取和写回数据. C.业务逻辑. D.数据展示. E.界面与逻辑的交互. A,B两部分的算法一般都非常稳定,不会轻易去改动,复用性也很高:C处与客户需求最紧密,最复杂,变化最大,大多少算法都集中在这里.D,E负责UI和逻辑的交互,也占有一定量的

WPF之Binding深入探讨

1,Data Binding在WPF中的地位 程序的本质是数据+算法.数据会在存储.逻辑和界面三层之间流通,所以站在数据的角度上来看,这三层都非常重要.但算法在3层中的分布是不均匀的,对于一个3层结构的程序来说,算法一般分布在这几处: A.数据库内部. B.读取和写回数据. C.业务逻辑. D.数据展示. E.界面与逻辑的交互. A,B两部分的算法一般都非常稳定,不会轻易去修改,复用性也非常高:C处与客户需求最紧密,最复杂,变化最大,大多少算法都集中在这里.D,E负责UI和逻辑的交互,也占有一定

WPF之Binding【转】

WPF之Binding[转] 看到WPF如此之炫,也想用用,可是一点也不会呀. 从需求谈起吧: 首先可能要做一个很炫的界面.见MaterialDesignInXAMLToolKit. 那,最主要的呢,还是想显示数据. 就先来数据,数据可以从数据库里得到,可是如何显示出来呢? 本文的主角出来了:(以下内容全转载他人,具体 原文见上面的链接) ==================================== 1,Data Binding在WPF中的地位 程序的本质是数据+算法.数据会在存储.

WPF QuickStart系列之数据绑定(Data Binding)

这篇博客将展示WPF DataBinding的内容. 首先看一下WPF Data Binding的概览, Binding Source可以是任意的CLR对象,或者XML文件等,Binding Target需要有依赖属性.这样便可以进行Data Binding.请看下面的示例, C# public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); DataContext = new Pe