在数据绑定时,还有一项工作,就是数据的验证。

在上一篇博文中,文本框来显示person对象的年龄,我们知道,因为年龄是数据,在输入非数字时,一定是不符合现实意义的。在WPF中,对数据的绑定,特别是双向绑定,数据目标属性更新数据源属性时,进行数据验证,道先,如果给一个绑定目标属性加入验证,必需用属性元素语法来达到目的,就是给Binding属性添加一个<Binding.ValudationRules>,再添加一个<ExceptionValidationRule>的子标签,同时把<Binding>标签的NotifyOnValidationError的属性设置成true,这样,后台的验证代码才起作用(因为默认情况下NotifyOnValidationError是 false)

<TextBox Height="23" Margin="12,31,0,0" Name="textBox1" ;120" >

<TextBox.Text>

<Binding Path="Age" Mode="TwoWay" NotifyOnValidationError="True" UpdateSourceTrigger="PropertyChanged">

<Binding.ValidationRules>

<ExceptionValidationRule>

</ExceptionValidationRule>

</Binding.ValidationRules>

</Binding>

</TextBox.Text>

</TextBox>

上面XAML代码与下面代码功能相同:

Binding bd = new Binding("Age");

bd.Mode = BindingMode.TwoWay;

bd.NotifyOnValidationError = true;

bd.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;

ValidationRule vr = new ExceptionValidationRule();

bd.ValidationRules.Add(vr);

textBox1.SetBinding(TextBox.TextProperty, bd);

然后把验证失败后的执行操作代码,关联起来,如下:

Validation.AddErrorHandler(textBox1, valite);

//下面是验证失败后的代码。

public void valite(object sender, ValidationErrorEventArgs e)

{

if (e.Action == ValidationErrorEventAction.Added)

{

MessageBox.Show((string)e.Error.ErrorContent, "验¨|证?è错?¨a误¨?!ê?");

}

}

在验证数据时,我们用的是系统自定义的验证类ValidationRule,这个类是个抽象类,目前的版本微软提供了两个子类来实现它,一个是ExceptionValidationRule,另一个是DataErrorValidationRule,前者是捕获在绑定目标属性把数据更新回绑定源属性时的异常,后者是检查绑定源提供用户界面可以绑定的自定义错误信息。本例子中,用的是ExceptionValidationRule,实际上就是把一些非int类型的数值给person.Age赋值时,触发异常,ExceptionValidationRule就捕获到异常,从而通过Validation.AddErrorHandler来报出异常。

另外,即然ValidationRule是抽象类,ExceptionValidationRule和DataErrorValidationRule都是继承它来的,能不能我们也定义一个类去继承ValidationRule,来完成自己的验证呢,答案是肯定的,就拿上面的例子来说Age,一般是在0到150之间(人的年龄一般没有负的,人的年龄指活的时长,也就是人最多活150岁,这个值已经相当对得起活着的人了),那就可以自定义一个类,继承ValidationRule,来实现这个年龄范围的业务。

首先来定义这个类:

public class ValidationAge:ValidationRule

{

public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)

{

int age;

if (!int.TryParse(value.ToString(), out age))

{

return new ValidationResult(false, "年龄类型不正确!");

}

else

{

if (age < 0 || age > 150)

{

return new ValidationResult(false, "不符合人类的年龄(0-150)!");

}

else

{

return new ValidationResult(true, null);

}

}

}

}

XAML代码如下:

<TextBox Height="23" Margin="12,31,0,0" Name="textBox1" ;120" >

<TextBox.Text>

<Binding Path="Age" Mode="TwoWay" NotifyOnValidationError="True" UpdateSourceTrigger="PropertyChanged">

<Binding.ValidationRules>

<m:ValidationAge>

</m:ValidationAge>

</Binding.ValidationRules>

</Binding>

</TextBox.Text>

</TextBox>

或用代码实现:

Binding bd = new Binding("Age");

bd.Mode = BindingMode.TwoWay;

bd.NotifyOnValidationError = true;

bd.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;

ValidationRule vr = new ValidationAge();

bd.ValidationRules.Add(vr);

textBox1.SetBinding(TextBox.TextProperty, bd);

如果抛开WPF绑定这个概念,上面所做的工作就是从UI的值往对象中回写的时候,如果不符合要求,就抛出异常,在 WinForm中,我们通常是给类的属性设置这些验证,就是在Person类的Age属性Set访问器中验证Value的有效性,当然,这种做完在WPF中也有效,相经较之下,这种做法还较简单。其实WPF中的这种做法,本质是一样的,就在是调用Person类的Age的Set访问器前,加了一个验证。

还有一点要注意的是在验证方法valite中,ValidationErrorEventArgs参数有一个Action的属性,这个属性是个枚举值,有两个枚举值,Added和Removed,如果不作处理,当我们输入一个非数字的值时,理所当然报错,但当我们删除这个值时也报错,就是因为才望高验证的触发是验证值的改变,添加和删除(没有修改,因为修改是一个删除+添加的过程),所以为了避免出来两次验证,就作了一下处理,只验证Action为Added的。

再有一点就是,如果绑定不做自定义验证VlidationAge,而是用ExceptionValidationRule,文本框与年龄绑定,因为年龄是整型,如果在文本框中输和e或E,都会转化成科学讲学法的,这点要注意。