传统的验证方法
开发应用程序最基本的的工作内容是进行数据验证。Silverlight的应用程序也不例外。Silverlight应用一定程度上类似于Windows Form应用。其数据验证可以用Winform传统的方法,如在准备提交时的代码中逐项检查数据的合法性。例如一个窗体中有若干输入框,和一个提交按钮。输入完毕后,点击提交按钮。我们可以在提交按钮Click事件处理程序中检查每个输入框的输入合法性。如果不合法,或者用一个弹出窗口提示用户,或者用别的一些方式(这些方式很多的,完全取决于UI设计),再之后就将光标停到有非法值的输入框,等待用户更正。
例如下面的代码:
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
xmlns:uriMapper="clr-namespace:System.Windows.Navigation;assembly=System.Windows.Controls.Navigation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
x:Class="SilverlightApplication1.MainPage"
mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
<Grid Height="480" x:Name="grid1" Width="640">
<sdk:Label Height="19" x:Name="label1" Width="52" Content="学生姓名" HorizontalContentAlignment="Left" HorizontalAlignment="Left" Margin="32,113,0,0" VerticalAlignment="Top" />
<TextBox Height="23" x:Name="textBox1" Width="110" HorizontalAlignment="Left" Margin="109,109,0,0" HorizontalContentAlignment="Left" VerticalAlignment="Top" />
<Button Content="打印" Height="24" x:Name="button1" Click="button1_Click" Margin="43,233,0,0" VerticalAlignment="Top" HorizontalAlignment="Left" Width="70" />
<ComboBox Height="24" HorizontalAlignment="Left" Margin="109,147,0,0" x:Name="comboBox1" VerticalAlignment="Top" Width="110">
<ComboBoxItem Content="男" />
<ComboBoxItem Content="女" />
</ComboBox>
<sdk:Label Content="性别" Height="19" HorizontalAlignment="Left" HorizontalContentAlignment="Left" Margin="32,147,0,0" x:Name="label2" VerticalAlignment="Top" Width="52" />
<sdk:Label Height="32" HorizontalAlignment="Left" Margin="68,35,0,0" x:Name="label3" VerticalAlignment="Top" Width="141" Content="学生档案管理" FontSize="16" />
<Button Content="保存" Height="23" Margin="134,233,0,0" x:Name="button2" VerticalAlignment="Top" Click="button2_Click" HorizontalAlignment="Left" Width="75" />
</Grid>
</UserControl>
cs代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Printing;
namespace SilverlightApplication1
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
}
void pDoc_PrintPage(object sender, PrintPageEventArgs e)
{
e.PageVisual = this;
//throw new NotImplementedException();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
PrintDocument pDoc = new PrintDocument();
pDoc.PrintPage += new EventHandler<PrintPageEventArgs>(pDoc_PrintPage);
pDoc.Print("打印本页");
}
private void button2_Click(object sender, RoutedEventArgs e)
{
if (textBox1.Text.Length == 0)
{
MessageBox.Show("学生姓名必须输入");
textBox1.Focus();
}
}
private void grid1_Loaded(object sender, RoutedEventArgs e)
{
grid1.DataContext = new StudentInfo();
}
}
}
这个验证是检查学生姓名是否输入了。如果没有输入就弹出一个消息窗口,要求输入学生姓名。如图:
再之后将光标定位到学生姓名输入框。这是最传统的Winform输入验证。我们还是可以沿用。
用数据绑定的方式来验证数据
除了传统的方法之外,微软还提供了另外一种方式来实现数据合法性的验证,即:用客户端数据绑定来验证输入的数据。下面介绍一下。通过定义实现接口INotifyPropertyChanged的数据对象,来实现数据验证。
首先创建数据对象,这个数据对象必须有get和set。
using System;
using System.ComponentModel;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace SilverlightApplication1
{
public class StudentInfo : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate { };
private string _name = null;
private byte _gender = 0;
public string Name
{
get
{
return _name;
}
set
{
if (string.IsNullOrEmpty(value))
throw new ArgumentException("Student Name is required.");
if (value.Length < 30)
throw new ArgumentException("Student Name is too long.");
_name = value;
OnPropertyChanged("name");
}
}
public byte Gender
{
get
{
return _gender;
}
set
{
_gender = value;
OnPropertyChanged("gender");
}
}
private void OnPropertyChanged(string propertyName)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
编译一下。保证编译成功。这对后面的只能提示有用。切记。
XAML页面修改如下:
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
xmlns:uriMapper="clr-namespace:System.Windows.Navigation;assembly=System.Windows.Controls.Navigation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" xmlns:local="clr-namespace:SilverlightApplication1"
x:Class="SilverlightApplication1.MainPage"
mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
<Grid Height="480" x:Name="grid1" Width="640" Loaded="grid1_Loaded">
<Grid.DataContext>
<local:StudentInfo/>
</Grid.DataContext>
<sdk:Label Height="19" x:Name="label1" Width="52" Content="学生姓名" HorizontalContentAlignment="Left" HorizontalAlignment="Left" Margin="32,113,0,0" VerticalAlignment="Top" />
<TextBox Height="23" x:Name="textBox1" Width="110" HorizontalAlignment="Left" Margin="109,109,0,0" HorizontalContentAlignment="Left" VerticalAlignment="Top" Text="{Binding Mode=TwoWay, UpdateSourceTrigger=Explicit, NotifyOnValidationError=True, ValidatesOnExceptions=True, Path=Name}" />
<Button Content="打印" Height="24" x:Name="button1" Click="button1_Click" Margin="43,233,0,0" VerticalAlignment="Top" HorizontalAlignment="Left" Width="70" />
<ComboBox Height="24" HorizontalAlignment="Left" Margin="109,147,0,0" x:Name="comboBox1" VerticalAlignment="Top" Width="110">
<ComboBoxItem Content="男" />
<ComboBoxItem Content="女" />
</ComboBox>
<sdk:Label Content="性别" Height="19" HorizontalAlignment="Left" HorizontalContentAlignment="Left" Margin="32,147,0,0" x:Name="label2" VerticalAlignment="Top" Width="52" />
<sdk:Label Height="32" HorizontalAlignment="Left" Margin="68,35,0,0" x:Name="label3" VerticalAlignment="Top" Width="141" Content="学生档案管理" FontSize="16" />
<Button Content="保存" Height="23" Margin="134,233,0,0" x:Name="button2" VerticalAlignment="Top" Click="button2_Click" HorizontalAlignment="Left" Width="75" />
</Grid>
</UserControl>
笔者看英文的书中介绍是用Express Blend来写黄色和蓝色背景部分。笔者试了一下,在Visual Studio里面似乎没有和Blend一样的操作界面。但是不想为了这个功能来装一个Blend。这里介绍一个只用Visual Studio就能实现的方法:
首先找到需要绑定的控件的容器,这里我们想把学生姓名绑定到textBox1,textBox1的容器是grid1。那么在这一行后回车:
输入<Grid.DataContext>,回车
再输入<local:,Visual Studio智能提示,我们从中选择StudentInfo,在输入/>。然后就成了:
<Grid.DataContext>
<local:StudentInfo/>
</Grid.DataContext>
在容器添加数据上下文之后。在找到textBox1的Text属性。选”Appy Data Binding”.
Source这一页,StudentInfo已经作为DataContext,什么都不用变。点Path页,
选择Name,这里没有Converter,就直接选择Options页。
Mode选TwoWay,UpdateSourceTrigger选:Explicit,勾选NotifiyOnValidationError,ValidatesOnExceptions, ValidateOnNotifyDataErrors。
然后形成了上述XAML的蓝色背景部分。
之后再选grid1,加入grid1_Loaded处理程序。
private void grid1_Loaded(object sender, RoutedEventArgs e)
{
grid1.DataContext = new StudentInfo();
}
这是给grid1的DataContext初始化一个数据对象。
再在保存按钮的click时间处理程序中如此写:
private void button2_Click(object sender, RoutedEventArgs e)
{
var bindingExpression = textBox1.GetBindingExpression(TextBox.TextProperty);
bindingExpression.UpdateSource();
}
这是将textBox1的Text属性绑定到数据对象。 至此,我们可以运行此程序看看效果。因我们现在用的方法是在Set中抛出Exception。不能用Debug模式运行。请用Debug->Start without Debugging来启动程序。
姓名是必须输入的,试试不输入,直接点保存,姓名输入框会有一个红框:
鼠标移到其右上角的小三角处。
数据对象中抛出的Exception信息就会显示出来:
至此已经实现了一种根据数据对象绑定到Silverlight控件的数据验证方法。以上是在Set抛出例外的方法。
微软还为我们实现了一种DataAnnotation的机制。这样需要我们的工程引用System.ComponentModel.DataAnnotations.dll。还要加上:
using System.ComponentModel.DataAnnotations;
如下代码:
[Required(ErrorMessage = "请输入学生姓名")]
public string Name
{
get
{
return _name;
}
set
{
var validatorContext = new ValidationContext(this, null, null);
validatorContext.MemberName = "Name";
Validator.ValidateProperty(value, validatorContext);
_name = value;
}
}
又如加一个最长30个字符的限制:
[Required(ErrorMessage = "请输入学生姓名")]
[StringLength(30, ErrorMessage="最长30个字符")]
public string Name
{
get
{
return _name;
}
set
{
var validatorContext = new ValidationContext(this, null, null);
validatorContext.MemberName = "Name";
Validator.ValidateProperty(value, validatorContext);
_name = value;
}
}
以上是DataAnnotation方法来做输入验证。
Silverlight数据验证的安全性
上面说了这么些数据验证方法。都是在客户端浏览器里运行的验证程序。都是不安全的。Silverlight程序就象Javascript程序一样在客户端浏览器中运行。黑客可以解开xap包,修改Silverlight程序。这和黑客可以绕过Javascript是一样的道理。所以基于Silverlight的系统,都必须在服务器端的WCF和web应用中加入严格的数据验证。才能真正保证安全。