• MVVM之Validation


    Binding Validation Rules:

    <TextBox>
    <TextBox.Text>
    <Binding>
    <Binding.ValidationRules>
    <ExceptionValidationRule />
    </Binding.ValidationRules>
    </Binding>
    </TextBox.Text>
    </TextBox>

    在WPF的验证中引入了ValidationRule验证规则概念,提供一种为检查用户输入的有效性而创建自定义规则的方法。

    属性:
    ValidatesOnTargetUpdated  获取或设置一个值,该值指示当Binding 的目标更新时是否运行验证规则。
    ValidationStep(枚举)  获取或设置验证规则的运行时间。

    ValidationStep值:

    RawProposedValue           在进行任何转换之前运行ValidationRule
    ConvertedProposedValue  在转换了值后运行ValidationRule
    UpdatedValue                  在更新了源后运行ValidationRule
    CommittedValue              在将值提交到源后运行ValidationRule

    方法:
    Validate      当在派生类中重写时,对值执行验证检查(子类必须重写)。

    public abstract ValidationResult Validate(
    Object value,
    CultureInfo cultureInfo
    )

    该方法有两个参数,第一个为要检查的来自绑定目标的值;第二个为要在此规则中使用的区域性。


    先看个自定义ValidationRule例子:

      class MyValidationRule:ValidationRule
    {

    public int MinValue
    {
    get;
    set;
    }
    public int MaxValue
    {
    get;
    set;
    }
    public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
    {
    int price = 0;

    try
    {
    if (((string)value).Length > 0)
    price = Int32.Parse((String)value);
    }
    catch (Exception e)
    {
    return new ValidationResult(false, "Illegal characters or " + e.Message);
    }

    if ((price < MinValue) || (price > MaxValue))
    {
    return new ValidationResult(false,
    "Please enter an age in the range: " + MinValue + " - " + MaxValue + ".");
    }
    else
    {
    return new ValidationResult(true, null);
    }

    }
    }


    其实例子很简单,实现了ValidationRule类,同时定义了两个属性MinValue和MaxValue,用于在XAML中设定值的范围,当绑定控件的值超出这个范围将会出现错误。
    说到这里就要提到ValidationResult返回类型,表示ValidationRule返回的结果。指示选中值是否通过ValidationRuleValidate方法。

    构造函数:

    public ValidationResult(
    bool isValid,
    Object errorContent
    )

    第一个参数为根据 ValidationRule,选中值是否有效;第二个为有关无效性的信息。(错误的信息),可以看到上个自定义例子中如果验证通过则第一个参数为true,错误信息为空,如果验证失败,则第一个参数为false,第二个参数为错误信息。
    主要属性:
    ErrorContent 获取提供有关无效性的附加信息的对象,也就是错误信息。
    IsValid 获取一个值,该值指示根据ValidationRule,选中值是否有效。

    下面看下在XAML中的使用:

    <Window x:Class="View.Wpf.Validation.CustomeValidateRule"
    xmlns
    ="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x
    ="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:validation
    ="clr-namespace:View.Wpf.Validation"
    Title
    ="CustomeValidateRule" Height="300" Width="300">
    <Window.Resources>
    <validation:Product x:Key="product"></validation:Product>
    </Window.Resources>
    <Grid>
    <StackPanel>
    <TextBox Height="20" Width="200" Margin="0 10 0 0" >
    <TextBox.Text>
    <Binding Source="{StaticResource ResourceKey=product}" Path="Price" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged">
    <Binding.ValidationRules>
    <validation:MyValidationRule ValidatesOnTargetUpdated="True" MinValue="1" MaxValue="100"></validation:MyValidationRule>
    </Binding.ValidationRules>
    </Binding>
    </TextBox.Text>
    </TextBox>
    </StackPanel>
    </Grid>
    </Window>

    可以看到我们的代码和之前直接绑定TextBox.Text有区别的,现在我们每个节点都是独立的,因为要进行Binding其他属性的绑定。

    在Binding.ValidationRules中放置了我们自定义的ValidationRule类,同时设置属性MinValue和MaxValue值用于验证用户键入的值是否符合验证要求,也可以在这里设置ValidatesOnTragetUpdated和ValidationStep属性。
    我们设置Binding的UpdateSourceTrigger属性为PropertyChagned意为当属性改变后更新源,所以当我们在文本框中输入的数字不在1到100范围中则会出现默认的红色边框表示出现了验证错误。

     如果你感觉这个错误的效果不满意,没关系,可以自定义错误模板如下:

     其实我们的模板页很简单,放置了一个TextBlock显示文本为红色的叹号,同时模板中最重要的是AdornedElementPlaceholder标签(表示 ControlTemplate 中使用的元素,该元素用于指定修饰控件相对于 ControlTemplate 中的其他元素所放置的位置,仅当创建用作自定义验证 ErrorTemplate 的 ControlTemplate 以便在用户输入无效时提供可见反馈时,才使用此类.)

        <ControlTemplate x:Key="validateTemplate" >
    <DockPanel>
    <TextBlock Foreground="Red" FontSize="20"></TextBlock>
    <AdornedElementPlaceholder></AdornedElementPlaceholder>
    </DockPanel>
    </ControlTemplate>

    使用如下:

       <TextBox Height="20" Width="200" Margin="0 10 0 0" Validation.ErrorTemplate="{StaticResource ResourceKey=validateTemplate}">
    <TextBox.Text>
    <Binding Source="{StaticResource ResourceKey=product}" Path="Price" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged">
    <Binding.ValidationRules>
    <validation:MyValidationRule ValidatesOnTargetUpdated="True" MinValue="1" MaxValue="100"></validation:MyValidationRule>
    </Binding.ValidationRules>
    </Binding>
    </TextBox.Text>
    </TextBox>

    可以看到我们在TextBox的Validation.ErrorTemplate中指定为我们定义的错误模板,当验证出错时候则显示如下:

    可是这个错误总归有点不好,没有告诉用户出错的信息,所以我们继续修改,添加样式如下:

      <Style x:Key="textBoxInError" TargetType="{x:Type TextBox}">
    <Style.Triggers>
    <Trigger Property="Validation.HasError" Value="true">
    <Setter Property="ToolTip"
    Value
    ="{Binding RelativeSource={x:Static RelativeSource.Self},
    Path=(Validation.Errors)[0].ErrorContent}
    "/>
    </Trigger>
    </Style.Triggers>
    </Style>

    添加了TextBox的样式,同时指定Trigger的属性为Validation.HasError,即有错误发生,然后设置TextBox的ToolTip为错误的信息,也就是我们在ValidationResult的参数的值。
    指定TextBox的样式:

     <TextBox Height="20" Width="200" Margin="0 10 0 0" Style="{StaticResource textBoxInError}" Validation.ErrorTemplate="{StaticResource ResourceKey=validateTemplate}">
    <TextBox.Text>
    <Binding Source="{StaticResource ResourceKey=product}" Path="Price" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged">
    <Binding.ValidationRules>
    <validation:MyValidationRule ValidatesOnTargetUpdated="True" MinValue="1" MaxValue="100"></validation:MyValidationRule>
    </Binding.ValidationRules>
    </Binding>
    </TextBox.Text>
    </TextBox>

    效果如下,可以看到当鼠标放到文本框上会显示错误信息,也就是ValidationResult的参数的值。
     


     

    验证框架之IDataErrorInfo:
    如果你看过我之前一篇Silverlight之validation,那你则对INotifyDataErrorInfo有点印象的,该对象是在Silverlight中使用,而在WPF中则使用IDataErrorInfo对象。INotifyDataErrorInfo为异步执行,而IDataErrorInfo则为同步执行。
    接下来看看如何在MVVM中使用IDataErrorInfo完成验证:

    首先创建Model类如下:

     public class Customer : IDataErrorInfo
    {
    private string _name;
    private int _age;
    private static readonly char[] IllegalCharacters = new char[] { '_', '!', '@', '%' };
    public Customer(string name, int age)
    {
    Name = name;
    Age = age;
    }
    public string Name
    {
    get { return _name; }
    set { _name = value; }
    }
    public int Age
    {
    get { return _age; }
    set { _age = value; }
    }

    #region IDataErrorInfo Members

    public string Error
    {
    get { throw new NotImplementedException(); }
    }

    public string this[string columnName]
    {
    get
    {
    string error = string.Empty;
    switch (columnName)
    {
    case "Name":
    if (_name.IndexOfAny(IllegalCharacters) > 0)
    {
    error = string.Format("The customer's name contains illegalcharacters ({0})", new string(IllegalCharacters));
    }
    break;
    case "Age":
    if (_age < 18 || _age > 100)
    {
    error = "Customers must be 18 or over to shop here!";
    }
    break;
    default:
    break;
    }
    return error;
    }
    }

    #endregion
    }

    就是一个简单的Model类,实现了IDataErrorInfo接口,同时可以看到里边有一个索引器,在索引器中加上了对索引值(属性名称)的判断,当为Age时候则判断值在18到100,
    如果为Name则不能包含定义的字符串。
    或者在ViewModel实现该类,代码为下:

     public class CustomerViewModel : IDataErrorInfo,INotifyPropertyChanged
    {

    private static readonly char[] IllegalCharacters = new char[] { '_', '!', '@', '%' };
    private string _name;
    private int _age;
    private Customer _customer;
    public CustomerViewModel()
    {
    _customer = new Customer(Name, Age);
    }



    public string Name
    {
    get { return _name; }
    set { _name = value; }
    }


    public int Age
    {
    get { return _age; }
    set {
    OnPropertyChanged("Age");
    _age = value; }
    }

    #region IDataErrorInfo Members

    public string Error
    {
    get { throw new NotImplementedException(); }
    }

    public string this[string columnName]
    {
    get
    {
    string error = string.Empty;
    switch (columnName)
    {
    case "Name":
    if (_name.IndexOfAny(IllegalCharacters) > 0)
    {
    error = string.Format("The customer's name contains illegalcharacters ({0})", new string(IllegalCharacters));
    }
    break;
    case "Age":
    if (_age < 18 || _age > 100)
    {
    error = "Customers must be 18 or over to shop here!";
    }
    break;
    default:
    break;
    }
    return error;
    }
    }

    #endregion

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged(string propertyName)
    {
    if (PropertyChanged!=null)
    {
    PropertyChanged(this,new PropertyChangedEventArgs(propertyName));
    }
    }

    #endregion
    }

    其实验证的部分和Model是相同的,主要区别在ViewModel定义了Model的对象,用来初始化和操作,其他均为相同的原理。
    XAML的使用也很简单,有两个选择,使用DataErrorValidationRule规则,或者使用ValidatesOnDataErrors属性为ture(暗中已经添加了DataErrorValidationRule规则):

      <Grid>
    <Grid.Resources>
    <viewModel:CustomerViewModel x:Key="customer"></viewModel:CustomerViewModel>
    </Grid.Resources>
    <TextBox Height="20" Width="200">
    <TextBox.Text>
    <Binding Source="{StaticResource customer}" Path="Age" UpdateSourceTrigger="PropertyChanged">
    <Binding.ValidationRules>
    <DataErrorValidationRule ValidatesOnTargetUpdated="True"></DataErrorValidationRule>
    </Binding.ValidationRules>
    </Binding>
    </TextBox.Text>
    </TextBox>
    </Grid>

    上边为使用了DataErrorValidationRule规则,或者使用属性:

        <TextBox Height="20" Width="200">
    <TextBox.Text>
    <Binding Source="{StaticResource customer}" Path="Age" ValidatesOnDataErrors="True" UpdateSourceTrigger="PropertyChanged">
    </Binding>
    </TextBox.Text>
    </TextBox>

    两者的效果均为相同。

    以上为MVVM中的验证部分,如果需要更多的验证方法请参考本人的另一篇博客Silverlight之Validation




  • 相关阅读:
    如何学习一项新技术呢?
    开发人员如何构建自己的学习笔记系统
    Stream流之List、Integer[]、int[]相互转化
    图解LinkedHashMap原理
    java 手动实现单链表(尾插法和头插法)
    为什么你学不会递归?刷题几个月,告别递归,谈谈我的经验
    谈谈限流算法的几种实现
    使用LinkedHashMap实现一个简易的LRU缓存
    Java深拷贝和浅拷贝
    Excel两列求差集和并集的实现
  • 原文地址:https://www.cnblogs.com/ListenFly/p/2410302.html
Copyright © 2020-2023  润新知