CausesValidation、Validating和ErrorProvider提供了控件级验证的基础机制,我们可以用它们对控件逐一进行验证。
窗体级验证
Validating和ErrorProvider这对组合是一个不错的解决方案,可以在用户输入数据的时候进行验证。不幸的是,这种方法可能会使得我们无法进行窗体级的验证,而这在用户点击OK按钮提交数据时显然是必要的,因为用户在点击OK按钮前,有些控件可能未曾获得过焦点,它们的控件级验证代码也就不起作用了。先看看窗体级验证的代码:foreach (Control ctrl in this.Controls) ...{ ctrl.Focus(); if (!Validate()) ...{ this.DialogResult = DialogResult.None; return; } 但Cancel按钮就不需要实现窗体级的验证了,它的工作往往是简单地将窗体关闭。但是现在,如果当前拥有焦点的控件数据是无效的,Cancel按钮将不能点击,因为Cancel按钮的CausesValidation属性默认为true,焦点会一直停留在无效的控件上。我们只要将Cancel按钮的CausesValidation属性设置为false就好了。注意:这里的窗体应当是模式窗体,否则即使CausesValidation属性设置为false,也不能点击。
至此,使用数十行代码,我们的AddEmployee窗体就可以支持基本的验证了。
编程式验证 vs. 声明式验证
从生产力的角度来看,上面的解决方案有一个根本的问题:如果一个程序包含多个窗体,而每个窗体又包含多个控件,那么将需要大量的用于验证的代码。这些代码增大了UI的复杂性,使得程序难以维护,显式是应当避免的。一种方法是将那些通用的验证逻辑抽象为可重用的类型。有了这样的类型,还仅仅是第一步,它仍需要编写代码。{TODO}我们需要这样的解决方案:它具有Windows Forms UI的特点,因此Windows Forms组件或控件是我们不错的选择。以这种方式封装后,开发人员的工作就变成从工具箱上拖一个组件或控件放到窗体上,通过诸如属性浏览器(Property Browser)这样的设计期特性来配置它,然后让Windows Forms设计器帮我们将这些配置转换为代码,这些代码会出现在InitializeComponent方法中。这样原来的编程式(programmatic)体验变成了声明式(declarative)体验,而后者往往意味着高效。
添加设计期支持
第一步是添加设计期的支持,如果我们的实现不需要UI支持,可以从三种设计期组件继承:System.ComponentModel.Component, System.Windows.Forms.Control和 System.Windows.Forms.UserControl. Component,否则可以继承Control或UserControl。Control和UserControl的不同之处在于其呈现的方式,前者需要编写代码来呈现它,而后者则通过其它控件或组件来呈现它。我们在前面使用的验证代码没有绘制任何内容,而是借助于ErrorProvider来提示用户。因此,Component是我们最合适的选择。
Imitation Is the Sincerest Form of Flattery
下一步是要确定我们需要哪些种类的验证组件,可以参考一下ASP.NET中验证控件的实现机制。这样能保持一定的一致性,而且也不需要”重新发明轮子”了。这样那些ASP.NET的开发人员也更容易上手。ASP.NET现在提供了如下的验证控件:
验证控件
描述
RequiredFieldValidator
计算输入控件的值以确保用户输入值。
RegularExpressionValidator
计算输入控件的值,以确定该值是否与某个正则表达式所定义的模式相匹配。
CompareValidator
将由用户输入到输入控件的值与输入到其他输入控件的值或常数值进行比较。
RangeValidator
检查输入控件的值是否在指定的值范围内。
CustomValidator
对输入控件执行用户定义的验证。
同时我们还要考虑可扩展性,开发人员在必要的时候可以较为容易地开发自定义的验证组件。最后,这个实现应当利用Windows Forms中已有的验证机制(前面提及的部分)。
引入RequiredFieldValidator
有了上面的设计思路,现在要来点真的了。让我们从最简单的验证情形开始:RequiredFieldValidator。
建立一个Component类,名称为RequiredFieldValidator,其接口应当与ASP.NET中的对应类相同:
public partial class RequiredFieldValidator : Component ...{ string ControlToValidate ...{ get; set;} string ErrorMessage ...{ get; set;} string InitialValue ...{ get; set;} bool IsValid ...{ get; set;} void Validate(); }下面是每个成员的含义:
成员
描述
ControlToValidate
指定要验证的控件
ErrorMessage
控件未通过验证时显式的信息。
InitialValue
某些情况下,控件的默认值用作提示,如”请选择种类”,这时必填项意味着必须与默认值不同。此时用InitialValue。
IsValid
在调用Validate方法后报告控件的数据是否有效,默认为true。
Validate
验证指定控件的值,并设置IsValid。
在ASP.NET中,ControlToValidate是字符串类型的,这种间接的做法在基于请求、无状态的Web应用程序中是必要的。但在Windows Forms中我们则不必这么做,我们可以直接引用控件。同时,我们要在内部使用ErrorProvider组件,所以为其添加一个Icon属性:
public partial class RequiredFieldValidator : Component ...{ … Control ControlToValidate ...{ get; set;} Icon Icon ...{ get; set;} … }这种实现的关键在于如何挂接ControlValidate控件的Validating事件,这种做法与前面的控件级验证相一致,还有一个额外的好处,这里的ControlToValidate_Validating方法中,没有设置CancelEventArgs参数的Cancel属性,这样就不会把用户困在一个控件中。
组件的验证功能已经实现了,同时还为其添加了设计期支持。最终实现还提供了其它一些设计期特性:
- <!--[if !supportLists]-->指定了在属性浏览器中设置ControlToValidate时可以选择的控件种类;
- 在属性浏览器中隐藏了IsValid属性,因为它是运行时的属性。
编译,然后将组件添加到工具箱。
让我们回到前面的AddEmployee窗体,现在不再需要处理Validating事件了,只要拖3个组件到窗体,然后为它们设置属性。
<!--[if !vml]-->
<!--[endif]-->其中Phone Number域的验证组件的InitialValue为”Your number here.”。怎么样,是不是很high?
相关专题
- Windows操作系统安装 (15618篇文章)
- Windows权限设置 (10238篇文章)
- Windows操作系统安全集 (18679篇文章)
- Windows频道 (9812篇文章)
- 用vb.net实现闹钟提醒程序 (9次浏览)
- C#变得越来越臃肿是不可避免的? (7次浏览)
- .NET开发人员犯的6大安全错误 (6次浏览)
- C#与EXCEL的数据交互(一) (5次浏览)
- vb.net GDI+入门——画笔、画刷和颜色 (3次浏览)
- C# 3.0新特性之扩展方法 (1次浏览)
- VB.Net实现进程监视器的方法 (1次浏览)
- 请跟我来--使用Ext搞个原型 (1次浏览)
- ASP.NET 3.5 Extensions带来什么 (1次浏览)
- WPF的Attached属性 (0次浏览)




