前文介绍过Silverlight Validation中两个数据验证机制,ValidatesOnExceptions异常捕获验证机制和DataAnnotation验证机制,这两种验证机制,是在Silverlight 3 Validation Framework推出的,其运行方式类似,都是当异常抛出后,应用对异常信息进行捕获,并显示在客户端。在Silverlight 4中,Silverlight Validation有相对的改进,本篇将介绍Silverlight 4中新加入的验证机制功能,IDataErrorInfo客户端同步验证机制。
Silverlight 4 IDataErrorInfo接口概述
相信熟悉WPF的开发人员都知道,WPF也具有IdataErrorInfo接口,其接口可以无需抛出任何异常,即可对数据进行验证。而Silverlight的IDataErrorInfo接口就是从WPF中转化来的。与前面两种验证机制相比,Silverlight 4 IDataErrorInfo提供的同步验证方式,不再需要基于异常抛出的基础上来激活验证,简单的理解,就是当值验证失败的时候,不会抛出任何异常信息。这种验证机制,相对前两种验证机制来说更加灵活。
Silverlight 4 IDataErrorInfo接口类成员
IDataErrorInfo接口,位于System.ComponentModel命名空间。该接口主要被应用在需要对其进行验证的数据成员类。
代码
1
#region
IDataErrorInfo Members
2
3
public
string
Error
4
{
5
get
{
throw
new
NotImplementedException(); }
6
}
7
8
public
string
this
[
string
columnName]
9
{
10
get
{
throw
new
NotImplementedException(); }
11
}
12
13
#endregion
IDataErrorInfo接口具有两个属性:
1. Error: 该属性为验证设置属性错误提示信息;
2. Item: 该属性为错误信息集合,其中索引值为属性名,将其对应的错误信息,设置到指定的被验证控件中;
从MSDN对IdataErrorInfo接口的解释中可以看到,IDataErrorInfo接口,没有任何事件方法被预先定义。也就是说,IDataErrorInfo接口本身不具备自动激活验证功能。简单的可以理解成为,当验证错误产生时,该错误信息不会自动捕获,然后显示到用户客户端上。解决该问题,需要引入另外一个接口INotifyPropertyChanged。对Silverlight早期版本熟悉的开发人员,应该对INotifyPropertyChanged接口并不陌生,该接口主要功能是当数据成员改变时发出通知到客户端,是Silverlight中最常用的一个接口,这里对该接口不再赘述。
public
class
Customer : INotifyPropertyChanged
public
event
PropertyChangedEventHandler PropertyChanged;
private
void
NotifyPropertyChanged(String info)
{
if
(PropertyChanged
!=
null
)
{
PropertyChanged(
this
,
new
PropertyChangedEventArgs(info));
}
}
public
string
CustomerName
{
get
{
return
this
.customerNameValue;
}
set
{
if
(value
!=
this
.customerNameValue)
{
this
.customerNameValue
=
value;
NotifyPropertyChanged(
"
CustomerName
"
);
}
}
}
在使用了INotifyPropertyChanged接口后,每当数据成员验证错误产生时,都会执行IDataErrorInfo接口,检测Error和Item属性。但是仅仅使用INotifyPropertyChanged接口,还无法正常将错误信息显示在客户端,同时也需要在客户端添加新的绑定验证属性ValidatesOnDataErrors, 该属性在默认绑定情况下是False,如果使用IDataErrorInfo接口,则需要使用True,这时,客户端才会显示IDataErrorInfo接口被执行后产生的验证错误信息。
<
TextBox Text
=
"
{Binding CustomerName,Mode=TwoWay,ValidatesOnDataErrors=true}
"
/>
讲到这里,我们可以结合前两篇讲解的绑定验证属性,来理解IDataErrorInfo接口。
使用IDataErrorInfo接口,必须结合INotifyPropertyChanged接口使用,否则UI无法获取到相对应属性的错误关联信息。
而使用IDataErrorInfo接口绑定验证属性时,在客户端控件内容绑定中使用ValidatesOnDataErrors = True,
另外,如果需要使用BindingValidationError事件,在客户端控件内容中,需要再绑定NotifyOnValidationError = True,
如果需要对异常进行捕获相应,同时,也需要绑定ValidatesOnExceptions = True。
IDataErrorInfo接口使用实例演示
本实例仍旧使用上一篇的实例代码,在其基础上添加对IDataErrorInfo接口的调用,
首先在User数据成员类中,执行IDataErrorInfo和INotifyPropertyChanged接口
#region
IDataErrorInfo Members
private
string
_dataError
=
string
.Empty;
public
string
Error
{
get
{
return
_dataError; }
}
private
Dictionary
<
string
,
string
>
_dataErrors
=
new
Dictionary
<
string
,
string
>
();
public
string
this
[
string
columnName]
{
get
{
if
(_dataErrors.ContainsKey(columnName))
return
_dataErrors[columnName];
else
return
null
;
}
}
#endregion
#region
INotifyPropertyChanged Members
public
event
PropertyChangedEventHandler PropertyChanged;
protected
void
NotifyPropertyChanged(
string
propertyName)
{
if
(PropertyChanged
!=
null
)
PropertyChanged(
this
,
new
PropertyChangedEventArgs(propertyName));
}
#endregion
其中IDataErrorInfo中定义的Dictionary是验证错误集合。而INotifyPropertyChanged是最简单的当数据成员改变时返回通知到客户端,其中没有过多的逻辑代码。
我们使用最简单的数据成员生成一个验证错误,IDataErrorInfo捕获显示作为演示,
在User类中,添加一个Address地址数据成员,
private
string
_address;
public
string
address
{
get
{
return
_address;
}
set
{
_address
=
value;
}
}
然后在set中添加简单的逻辑代码,
private
string
_address;
public
string
address
{
get
{
return
_address;
}
set
{
if
(
string
.IsNullOrEmpty(value))
_dataErrors[
"
address
"
]
=
"
地址必须填写
"
;
else
if
(value.Trim().Length
<
6
)
_dataErrors[
"
address
"
]
=
"
地址至少6个字
"
;
else
if
(_dataErrors.ContainsKey(
"
address
"
))
_dataErrors.Remove(
"
address
"
);
_address
=
value;
NotifyPropertyChanged(
"
address
"
);
}
}
修改前台,添加新的输入框,另外绑定输入框内容到address,
<
StackPanel
Orientation
="Horizontal"
Margin
="5"
>
<
TextBlock
Text
="地 址: "
VerticalAlignment
="Center"
/>
<
TextBox
x:Name
="txtAddress"
Width
="200"
DataContext
="
{Binding Source={StaticResource UserDataContext}}
"
Text
="
{Binding Path=address, Mode=TwoWay, ValidatesOnDataErrors=True}
"
/>
</
StackPanel
>
其运行结果为:
下面,我们做一个较为复杂一些的数据验证,
学生成绩对应表
A - 90-100分
B - 80-89分
C - 70-79分
D - 60-69分
F - 0-59分
在这个实例中,我们对多个数据成员进行客户端数据验证。
添加两个新数据成员,gradelevel和graderange,其中使用独立的验证函数ValidateGradeLevelandRange进行数据验证,
private
string
_gradelevel;
public
string
gradelevel
{
get
{
return
_gradelevel; }
set
{
if
(ValidateGradeLevelandRange(value,graderange))
{
_gradelevel
=
value;
NotifyPropertyChanged(
"
gradelevel
"
);
}
}
}
private
decimal
_graderange;
public
decimal
graderange
{
get
{
return
_graderange; }
set
{
if
(ValidateGradeLevelandRange(gradelevel, value))
{
_graderange
=
value;
NotifyPropertyChanged(
"
graderange
"
);
}
}
}
#region
Customize Validation
private
bool
ValidateGradeLevelandRange(
string
level,
decimal
range)
{
bool
isValid
=
false
;
if
(level
==
null
)
{
_dataErrors[
"
gradelevel
"
]
=
"
成绩等级必须是A,B,C,D,F
"
;
return
false
;
}
else
{
switch
(level.ToUpper())
{
case
"
A
"
:
isValid
=
(range
>=
90
&&
range
<=
100
);
break
;
case
"
B
"
:
isValid
=
(range
>=
80
&&
range
<=
89
);
break
;
case
"
C
"
:
isValid
=
(range
>=
70
&&
range
<=
79
);
break
;
case
"
D
"
:
isValid
=
(range
>=
60
&&
range
<=
69
);
break
;
case
"
F
"
:
isValid
=
(range
>=
0
&&
range
<=
59
);
break
;
default
:
_dataErrors[
"
gradelevel
"
]
=
"
成绩等级必须是A,B,C,D,F
"
;
return
false
;
}
}
if
(isValid)
{
if
(_dataErrors.ContainsKey(
"
gradelevel
"
))
_dataErrors.Remove(
"
gradelevel
"
);
if
(_dataErrors.ContainsKey(
"
graderange
"
))
_dataErrors.Remove(
"
graderange
"
);
}
else
{
_dataErrors[
"
gradelevel
"
]
=
"
成绩等级和成绩范围不符合
"
;
_dataErrors[
"
graderange
"
]
=
"
成绩范围和成绩等级不符合
"
;
}
return
isValid;
}
#endregion
在前台添加两个成绩输入框,
<
StackPanel
Orientation
="Horizontal"
Margin
="5"
>
<
TextBlock
Text
="成绩等级: "
VerticalAlignment
="Center"
/>
<
TextBox
x:Name
="txtGradeLevel"
Width
="200"
DataContext
="
{Binding Source={StaticResource UserDataContext}}
"
Text
="
{Binding Path=gradelevel, Mode=TwoWay, ValidatesOnDataErrors=True, NotifyOnValidationError=False, ValidatesOnExceptions=True}
"
/>
</
StackPanel
>
<
StackPanel
Orientation
="Horizontal"
Margin
="5"
>
<
TextBlock
Text
="成绩范围: "
VerticalAlignment
="Center"
/>
<
TextBox
x:Name
="txtGradeRange"
Width
="200"
DataContext
="
{Binding Source={StaticResource UserDataContext}}
"
Text
="
{Binding Path=graderange, Mode=TwoWay, ValidatesOnDataErrors=True, NotifyOnValidationError=False, ValidatesOnExceptions=True}
"
/>
</
StackPanel
>
其最终运行结果如下:
今天内容讲到这里了。
源代码下载
欢迎大家加入"专注Silverlight" 技术讨论群:
32679955(六群)
23413513(五群)
32679922(四群)
100844510(三群)
37891947(二群)
22308706(一群)