相信很多人都听说过这句名言:garbage in ,garbage out ! 数据录入不规范(或错误)就象一颗定时炸弹,迟早会给系统带来麻烦,所以在数据录入时做好验证是很有必要的。
相对传统asp.net开发而言,SL4中的数据验证要轻松很多(主要得益于Xaml的Binding特性),步骤如下:
1、定义业务Model类时,在需要验证的属性setter中,写好业务逻辑,对于不合规范的value,要抛出异常!
同时切记Model类要实现INotifyPropertyChanged接口,同时每个setter方法的最后,要显示调用OnPropertyChanged方法
比如,我们要做一个会员注册填写资料的Form,需要一个UserModel类,为了方便,先定义一个通用的基类BusinessBaseObject.cs
代码语言:javascript复制using System.ComponentModel;
namespace BusinessObject
{
public class BusinessBaseObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// 属性改变时触发事件
/// </summary>
/// <param name="propertyName">Property that changed.</param>
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
再来定义UserModel.cs
代码语言:javascript复制#define DEV
//#undef DEV
using System;
using Common.Silverlight;
namespace BusinessObject
{
public class UserModel : BusinessBaseObject
{
private string _userName;
public string UserName
{
get { return _userName; }
set
{
#if DEV
if (string.IsNullOrEmpty(value))
{
throw new Exception("用户名不能为空");
}
if (!(value.IsEmail() || value.IsUserName()))
{
throw new Exception("格式错误,正确示例如:abc007");
}
#endif
_userName = value;
OnPropertyChanged("UserName");
}
}
private bool? _sex = null;
public bool? Sex
{
get { return _sex; }
set {
#if DEV
if (!_sex.HasValue)
{
throw new Exception("请选择性别");
}
#endif
_sex = value;
OnPropertyChanged("UserName");
}
}
private string _password;
public string Password
{
get { return _password; }
set
{
if (string.IsNullOrEmpty(value))
{
throw new Exception("密码不能为空!");
}
#if DEV
if (value.Length < 6)
{
throw new Exception("密码长度不得低于6位");
}
#endif
_password = value;
OnPropertyChanged("Password");
}
}
private string _password2;
public string Password2
{
get { return _password2; }
set
{
if (string.IsNullOrEmpty(value))
{
throw new Exception("密码不能为空");
}
#if DEV
if (value!=this._password)
{
throw new Exception("重复密码必须与密码输入一致");
}
#endif
_password2 = value;
OnPropertyChanged("Password2");
}
}
private DateTime _birthday;
public DateTime Birthday
{
get { return _birthday; }
set
{
#if DEV
if (value <= DateTime.Now.AddYears(-100))
{
throw new Exception("我们不赞同百岁以上的老人上网");
}
#endif
_birthday = value;
OnPropertyChanged("Birthday");
}
}
private string _email;
public string Email
{
get { return _email; }
set
{
#if DEV
if (!string.IsNullOrEmpty(value) && !value.IsEmail())
{
throw new Exception("格式错误,正确示例如:yjmyzz@126.com");
}
#endif
_email = value;
OnPropertyChanged("Email");
}
}
private string _telephone;
public string Telephone
{
get { return _telephone; }
set
{
#if DEV
if (!value.IsTel())
{
throw new Exception("格式错误,正确示例如:021-38889088或021-36559079-023");
}
#endif
_telephone = value;
OnPropertyChanged("Telephone");
}
}
private string _mobile = "";
public string Mobile
{
get { return _mobile; }
set
{
#if DEV
if (!value.IsMobile())
{
throw new Exception("格式错误,正确示例如:13916752109");
}
#endif
this._mobile = value;
OnPropertyChanged("Mobile");
}
}
private bool _agree = false;
public bool Agree
{
get { return _agree; }
set {
#if DEV
if (value==false)
{
throw new Exception("您必须同意注册条款!");
}
#endif
this._agree = value;
OnPropertyChanged("Agree");
}
}
private int _internetAge = 0;
public int InternetAge
{
get { return _internetAge; }
set {
#if DEV
if (_internetAge<0)
{
throw new Exception("网龄不能为负数");
}
#endif
_internetAge = value;
OnPropertyChanged("InternetAge");
}
}
private TimeRange _internetHours = new TimeRange();
public TimeRange InternetHours
{
get { return _internetHours; }
set {
_internetHours = value;
OnPropertyChanged("InternetHours");
}
}
}
public class TimeRange : BusinessBaseObject
{
private TimeSpan start = DateTime.Now.TimeOfDay TimeSpan.FromMinutes(5);
private TimeSpan end = DateTime.Now.TimeOfDay TimeSpan.FromHours(1);
private const int MaximumRangeSpan = 6;
public TimeSpan Start
{
get
{
return this.start;
}
set
{
if (value < DateTime.Now.TimeOfDay)
{
throw new Exception("上网时段起始值必须在当前时间5分钟以后");//注:这个限定只是为了演示数据验证,并无实际意义
}
if (End - value > TimeSpan.FromHours(MaximumRangeSpan))
{
ThrowOutOfRangeException();
}
this.start = value;
OnPropertyChanged("Start");
}
}
public TimeSpan End
{
get
{
return this.end;
}
set
{
if (value < DateTime.Now.TimeOfDay)
{
throw new Exception("上网时段截止值不能早于当前时间");//注:这个限定只是为了演示数据验证,并无实际意义
}
if (value - Start > TimeSpan.FromHours(MaximumRangeSpan))
{
ThrowOutOfRangeException();
}
this.end = value;
OnPropertyChanged("End");
}
}
private static void ThrowOutOfRangeException()
{
string message = string.Format("上网时间不能大于 {0} 小时", MaximumRangeSpan);
throw new Exception(message);
}
}
}
注:因为Sl中的数据验证实际上是通过抛异常将提示信息扔到前端的,这会导致在调试时vs.net不断的被异常信息所打断,这个有点烦人,所以我用了#define/#undef/#if /#endif 条件编译的小技巧,在调试期先不处理异常,等其它业务逻辑写完后,最后再加上#undef,进行数据验证测试。
2、xaml界面部分,用Binding将各控件与Model实例的属性关联,对于指定长度和指定输入字符集的字段(比如:18位身份证号,手机号之类),最适合用RadMaskedTextBox,示例如下:
代码语言:javascript复制<UserControl xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:validation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.Input"
xmlns:System="clr-namespace:System;assembly=mscorlib" xmlns:Telerik_Windows_Controls_MaskedTextBox="clr-namespace:Telerik.Windows.Controls.MaskedTextBox;assembly=Telerik.Windows.Controls.Input" xmlns:Telerik_Windows_Controls_Chromes="clr-namespace:Telerik.Windows.Controls.Chromes;assembly=Telerik.Windows.Controls" x:Class="Telerik.Sample.Validataion"
mc:Ignorable="d"
d:DesignHeight="600" d:DesignWidth="600">
<UserControl.Resources>
<Style x:Key="PasswordBoxToRadMaskTextBoxStyle" TargetType="PasswordBox">
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Background" Value="#FFFFFFFF"/>
<Setter Property="Foreground" Value="#FF000000"/>
<Setter Property="Padding" Value="2"/>
<Setter Property="BorderBrush" Value="#FF848484" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="PasswordBox">
<Grid x:Name="RootElement">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="MouseOver">
<Storyboard>
<ColorAnimation Duration="0" To="#FFFFC92B" Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)" Storyboard.TargetName="Border"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="DisabledVisualElement"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="FocusStates">
<VisualState x:Name="Focused">
<Storyboard>
<ColorAnimation Duration="0" To="#FFFFC92B" Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)" Storyboard.TargetName="Border"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Unfocused">
<Storyboard>
<ColorAnimation Duration="0" To="{TemplateBinding BorderBrush}" Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)" Storyboard.TargetName="Border"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="ValidationStates">
<VisualState x:Name="Valid"/>
<VisualState x:Name="InvalidUnfocused">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="ValidationErrorElement">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="InvalidFocused">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="ValidationErrorElement">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="IsOpen" Storyboard.TargetName="validationTooltip">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<System:Boolean>True</System:Boolean>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border x:Name="Border" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" CornerRadius="1" Opacity="1" BorderBrush="{TemplateBinding BorderBrush}">
<Border x:Name="ContentElement" Margin="{TemplateBinding Padding}"/>
</Border>
<Border x:Name="DisabledVisualElement" BorderBrush="#A5F7F7F7" BorderThickness="{TemplateBinding BorderThickness}" Background="#A5F7F7F7" IsHitTestVisible="False" Opacity="0"/>
<Border x:Name="ValidationErrorElement" BorderBrush="#FFDB000C" BorderThickness="1" CornerRadius="1" Visibility="Collapsed">
<ToolTipService.ToolTip>
<ToolTip x:Name="validationTooltip" DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}" Placement="Right" PlacementTarget="{Binding RelativeSource={RelativeSource TemplatedParent}}" Template="{StaticResource ValidationToolTipTemplate}">
<ToolTip.Triggers>
<EventTrigger RoutedEvent="Canvas.Loaded">
<BeginStoryboard>
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="IsHitTestVisible" Storyboard.TargetName="validationTooltip">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<System:Boolean>true</System:Boolean>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</ToolTip.Triggers>
</ToolTip>
</ToolTipService.ToolTip>
<Grid Background="Transparent" HorizontalAlignment="Right" Height="12" Margin="1,-4,-4,0" VerticalAlignment="Top" Width="12">
<Path Data="M 1,0 L6,0 A 2,2 90 0 1 8,2 L8,7 z" Fill="#FFDC000C" Margin="1,3,0,0"/>
<Path Data="M 0,0 L2,0 L 8,6 L8,8" Fill="#ffffff" Margin="1,3,0,0"/>
</Grid>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White">
<Grid VerticalAlignment="Center" HorizontalAlignment="Center" Width="500">
<Grid.RowDefinitions>
<RowDefinition Height="30" />
<RowDefinition Height="30" />
<RowDefinition Height="30" />
<RowDefinition Height="30" />
<RowDefinition Height="30" />
<RowDefinition Height="30" />
<RowDefinition Height="30" />
<RowDefinition Height="30" />
<RowDefinition Height="30" />
<RowDefinition Height="50" />
<RowDefinition Height="30" />
<RowDefinition Height="Auto"/>
<RowDefinition Height="30" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="400" MinWidth="200"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" TextAlignment="Right" VerticalAlignment="Center"><Run Text="用户名:"/></TextBlock>
<telerik:RadMaskedTextBox Grid.Column="1" Grid.Row="0" x:Name="txtUserName" VerticalAlignment="Center" GotFocus="RadMaskedTextBox_GotFocus"
Value="{Binding UserName, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnExceptions=True}" EmptyContent="格式:2-50位字母、数字、下线线组合" Width="250" MaskType="None" HorizontalAlignment="Left" />
<TextBlock Grid.Row="1" Grid.Column="0" TextAlignment="Right" VerticalAlignment="Center"><Run Text="密码:"/></TextBlock>
<PasswordBox Grid.Row="1" Grid.Column="1" Width="250" MaxLength="32" HorizontalAlignment="Left" VerticalAlignment="Center" Password="{Binding Password, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnExceptions=True}" x:Name="txtPwd" Style="{StaticResource PasswordBoxToRadMaskTextBoxStyle}" />
<TextBlock Grid.Row="2" Grid.Column="0" TextAlignment="Right" VerticalAlignment="Center"><Run Text="重复密码:"/></TextBlock>
<PasswordBox Grid.Row="2" Grid.Column="1" Width="250" HorizontalAlignment="Left" VerticalAlignment="Center" Password="{Binding Password2, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnExceptions=True}" x:Name="txtPwd2" Style="{StaticResource PasswordBoxToRadMaskTextBoxStyle}"/>
<TextBlock Grid.Row="3" Grid.Column="0" TextAlignment="Right" VerticalAlignment="Center"><Run Text="性别:"/></TextBlock>
<StackPanel Grid.Row="3" Grid.Column="1" Orientation="Horizontal" >
<telerik:RadComboBox x:Name="rcboSex" Width="250" SelectedIndex="2">
<telerik:RadComboBoxItem Content="男" />
<telerik:RadComboBoxItem Content="女" />
<telerik:RadComboBoxItem Content="保密" />
</telerik:RadComboBox>
</StackPanel>
<TextBlock Grid.Row="4" Grid.Column="0" TextAlignment="Right" VerticalAlignment="Center"><Run Text="电话号码:"/></TextBlock>
<telerik:RadMaskedTextBox Grid.Column="1" Grid.Row="4" MaskType="None" ValueChanging="txtTel_ValueChanging" x:Name="txtTel" VerticalAlignment="Center" GotFocus="RadMaskedTextBox_GotFocus"
Value="{Binding Telephone, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnExceptions=True}" EmptyContent="格式:区号-电话号码-分机号(可选)" Width="250" HorizontalAlignment="Left" />
<TextBlock Grid.Row="5" Grid.Column="0" TextAlignment="Right" VerticalAlignment="Center"><Run Text="手机号码:"/></TextBlock>
<telerik:RadMaskedTextBox Grid.Column="1" Grid.Row="5" Mask="###########" x:Name="txtMobile" VerticalAlignment="Center" GotFocus="RadMaskedTextBox_GotFocus"
Value="{Binding Mobile, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnExceptions=True}" EmptyContent="比如:13916752109" Width="250" HorizontalAlignment="Left" />
<TextBlock Grid.Row="6" Grid.Column="0" TextAlignment="Right" VerticalAlignment="Center"><Run Text="电子邮箱:"/></TextBlock>
<telerik:RadMaskedTextBox Grid.Column="1" Grid.Row="6" MaskType="None" x:Name="txtEmail" VerticalAlignment="Center" GotFocus="RadMaskedTextBox_GotFocus"
Value="{Binding Email, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnExceptions=True}" EmptyContent="比如:youname@sample.com" Width="250" HorizontalAlignment="Left" />
<TextBlock Grid.Row="7" Grid.Column="0" TextAlignment="Right" VerticalAlignment="Center"><Run Text="出生日期:"/></TextBlock>
<telerik:RadDatePicker Grid.Column="1" Grid.Row="7"
SelectedDate="{Binding Birthday, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnExceptions=True}"
Width="250" Height="22" HorizontalAlignment="Left" x:Name="rdp_Birthday" />
<TextBlock Grid.Row="8" Grid.Column="0" TextAlignment="Right" VerticalAlignment="Center"><Run Text="网龄:"/></TextBlock>
<telerik:RadNumericUpDown Grid.Row="8" Grid.Column="1" Minimum="0" Maximum="30"
Value="{Binding InternetAge, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnExceptions=True}"
Loaded="btnInternetAge_Loaded" SmallChange="1" HorizontalAlignment="Left" VerticalAlignment="Center"
Width="250" x:Name="btnInternetAge" />
<TextBlock Grid.Row="9" Grid.Column="0" TextAlignment="Right" VerticalAlignment="Center"><Run Text="经常上网时段:"/></TextBlock>
<StackPanel Grid.Row="9" Grid.Column="1">
<telerik:RadSlider IsSelectionRangeEnabled="True"
Style="{StaticResource RadSliderStyle}" Minimum="0" Maximum="1380"
TickFrequency="240"
SelectionStart="{Binding InternetHours.Start, Converter={StaticResource TimeConverter}, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnExceptions=True}"
SelectionEnd="{Binding InternetHours.End, Converter={StaticResource TimeConverter}, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnExceptions=True}"
TickPlacement="TopLeft" Width="250" HorizontalAlignment="Left">
<telerik:RadSlider.TickTemplate>
<DataTemplate>
<Grid>
<TextBlock FontSize="8" UseLayoutRounding="True"
Text="{Binding Converter={StaticResource TickValueConverter}}" />
</Grid>
</DataTemplate>
</telerik:RadSlider.TickTemplate>
</telerik:RadSlider>
<StackPanel Orientation="Horizontal" Width="200"
UseLayoutRounding="True" HorizontalAlignment="Left" Margin="25,0,0,0">
<TextBlock FontSize="8"
Text="{Binding InternetHours.Start, Converter={StaticResource TimeToTextConverter}}" />
<TextBlock FontSize="8" Text=" - " />
<TextBlock FontSize="8"
Text="{Binding InternetHours.End, Converter={StaticResource TimeToTextConverter}}" />
</StackPanel>
</StackPanel>
<telerik:RadToggleButton Grid.Row="10" Width="250" HorizontalAlignment="Left" Grid.Column="1" Content="我同意注册条款" VerticalAlignment="Center" IsChecked="{Binding Agree, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnExceptions=True}" x:Name="btnAgree" />
<validation:ValidationSummary Grid.Row="11" Grid.Column="0" Grid.ColumnSpan="2" AllowDrop="True" Header="提示" x:Name="ValidationSummary1"/>
<telerik:RadButton Content="提交" Grid.Row="12" Grid.Column="1" Height="22" Width="65" HorizontalAlignment="Left" Click="btnSubmit_Click" x:Name="btnSubmit" Margin="190,0,0,0"/>
</Grid>
</Grid>
</UserControl>
3、Xaml.cs后端部分
代码语言:javascript复制using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System;
using BusinessObject;
using Telerik.Windows.Controls;
using System.Text.RegularExpressions;
namespace Telerik.Sample
{
public partial class Validataion : UserControl
{
UserModel model = new UserModel();
public Validataion()
{
InitializeComponent();
this.Loaded = new RoutedEventHandler(Validataion_Loaded);
}
void Validataion_Loaded(object sender, RoutedEventArgs e)
{
this.DataContext = model;
#region
//model.Mobile = "13916752109";
//model.UserName = "yjmyzz";
//model.Password = "123456";
//model.InternetAge = 10;
//model.Birthday = new DateTime(1979, 6, 5);
//model.Telephone = "021-36559079";
//model.Email = "yjmyzz@126.com";
//model.Password2 = model.Password;
#endregion
#region 有错误时,不允许提交(必须配合输入框获取焦点时,自动激活验证)
Binding binding = new Binding("HasErrors");
binding.Source = ValidationSummary1;
binding.Converter = new HasErrorsToIsEnabledConverter();
this.btnSubmit.SetBinding(Button.IsEnabledProperty, binding);
#endregion
}
private void RadMaskedTextBox_GotFocus(object sender, RoutedEventArgs e)
{
//文本框获得焦点时,自动激活实时验证
UpdateBindingExpression(sender as DependencyObject, RadMaskedTextBox.ValueProperty);
}
private void UpdateBindingExpression(DependencyObject obj, DependencyProperty prop)
{
BindingExpression b = obj.ReadLocalValue(prop) as BindingExpression;
b.UpdateSource();
}
private void btnSubmit_Click(object sender, RoutedEventArgs e)
{
//显式验证必填项
UpdateBindingExpression(txtUserName, RadMaskedTextBox.ValueProperty);
UpdateBindingExpression(txtPwd, PasswordBox.PasswordProperty);
UpdateBindingExpression(txtPwd2, PasswordBox.PasswordProperty);
UpdateBindingExpression(this.btnAgree, RadToggleButton.IsCheckedProperty);
}
private void btnInternetAge_Loaded(object sender, RoutedEventArgs e)
{
//去掉小数位
(sender as RadNumericUpDown).NumberFormatInfo.NumberDecimalDigits = 0;
}
private void txtTel_ValueChanging(object sender, RadMaskedTextBoxValueChangingEventArgs e)
{
//只能输入"0-9"和下划线
e.Handled = new Regex(@"^[d-]{0,}$").IsMatch(e.NewMaskedText) == false;
}
}
}
运行截图:
在线演示地址:
http://img.24city.com/jimmy/sl4/controls/telerik.html