winform控件驗證技術

winform控件驗證技術

Windows 窗體驗證的主要功能

簡單地說,驗證是指在進行後續處理或存儲之前,確保數據的完整性和準確性的過程。對於數據驗證,有一條基本原則:”不要讓野蠻人進門” ,即必須在表示層及早對用戶輸入的數據進行驗證,以構成前沿驗證防禦。利用 UI,開發人員通常可以爲最終用戶構造一個更具人性化、響應性更高並提供更多信息的驗證過程,同時還可以避免出現類似於跨 N 層應用程序進行不必要的雙向網絡通信這樣的問題。

考慮圖 1 所示窗體:

 

圖 1. 要求驗證的 Add New Employee 窗體

該窗體需要驗證以下內容: 

必須輸入“姓名”、和“手機號碼” 

 

“手機號碼”必須爲11位數字

 

實現此驗證需要一個相應的基礎結構,WinForm 窗體提供了該基礎結構,並將其直接內置於每個控件中。爲指示控件支持驗證,將控件 CausesValidation 的屬性設置爲 True,即所有控件的默認值。 如果某個控件的CausesValidation 屬性設置爲 True,則當焦點切換到另一個CausesValidation 值也設置爲 True 的控件時,將引發前一個控件的Validating 事件。隨後,應處理Validating 以實現驗證邏輯,如確保提供“姓名”。 另外,爲了在驗證未能通過的時候,給用戶以醒目提示,需要將控件和ErrorProvider組件相結合來使用,示例代碼如下

void txtName_Validating(object sender, CancelEventArgs e) {

 // 要求輸入姓名

 if(txtName.Text.Trim() == "" ) {

e.Cancel = true;

errProvider.SerError(txtName,”姓名必須要輸入!”)

    return;

 }  else {

          errProvider.SerError(txtName,””);

}

}

代碼1 控件Validating事件處理過程

當在姓名文本框中沒有輸入內容時,顯示的界面如圖2所示。


 

圖 2.  結合ErrorProvider的驗證提示


窗體範圍的驗證

Validating 事件和ErrorProvider 組件的組合提供了一個優秀的解決方案,可以在需要時(即當用戶輸入數據時)動態驗證每個控件。遺憾的是,對Validating 事件的依賴使該解決方案無法自動擴展爲支持窗體範圍的驗證(當用戶單擊“確定” 按鈕完成數據輸入時,需要此驗證)。單擊”確定”按鈕前,一個或更多個控件可能沒有焦點,因此不引發其Validating 事件。窗體範圍的驗證通過手動調用捆綁在每個Validating 事件中的驗證邏輯來實現,方法是枚舉窗體中的所有控件,爲每個控件設置焦點,然後調用該窗體的Validate 方法,如下所示:

void btnOK_Click(object sender, System.EventArgs e) { foreach( Control control in Controls ) {    // 在控件上設置焦點    control.Focus();    // 驗證導致引發控件的驗證事件,    // 如果 CausesValidation 爲 True    if( !Validate() ) {
      errProvider.SetError(control,"錯誤提示信息");
      DialogResult = DialogResult.None;      return;    } else {
      errProvider.SetError(control , "");
    } 
   }}代碼2 “確定”按鈕單擊,窗體範圍數據驗證智能數據驗證框架

從工作效率的角度來看,該解決方案存在一個根本性的問題,即大型應用程序通常包含多個窗體,每個窗體通常比本文的小程序示例包含更多的控件,因此需要更多的驗證代碼。在 UI 日益複雜的情況下編寫大量相似的代碼是一項不具伸縮性的技術,因此應儘可能地避免。解決方案之一是編寫一個通用的驗證邏輯框架.應用該框架時,只要爲窗體範圍中的控件指定驗證規則,則數據驗證會自動在幕後進行。這有助於減少大量冗餘代碼,保持代碼的優雅和簡潔。

該驗證框架總體結構如圖3所示

 


                  圖3 數據驗證框架的總體結構



FormValidator是主類,該類用於對窗體進行自動驗證,其中:

Void DoValid()方法應該在類似用戶單擊”確定”按鈕的場景被調用,以實現窗體範圍的數據驗證,當應用該框架是,代碼2可以簡化爲:

void btnOK_Click(object sender, System.EventArgs e) {

     aFormValidator.ValidateAll(); //aFormValidator代表一個FormValidator對象

 }

                  代碼3 運用ValidateAll實現窗體範圍的數據驗證

此外,ValidateAll會指定跟蹤所有的需要驗證的控件,對不需要數據驗證的控件不會啓動驗證過程

Void SetupValidatorForControl(Conbtrol controlToValidate , params IValidator[] validators)方法爲每個需要數據驗證的控件安裝多個驗證器. 當我們需要對某個控件應用多個驗證規則進行數據驗證的時候,再也不需要處理Validating事件,框架使用者只需要窗體的初始化時(通常是FormLoading事件)中調用SetUpValidatorForControl方法即可,示例代碼如下:

void FormLoading(….) {

     SetupValidatorForControl(txtName,new RequireFieldValidator());

}

IValidator代表數據驗證器,方法IsValid(controlToValidate:Control)控件進行實際的驗證,如果controlToValidate控件通過該數據驗證器,則返回true,否則返回false;String ErrorMessage()返回當控件沒有通過該數據驗證器驗證時,應該顯示給用戶的提示信息.

AbstractValidator實現了Ivalidator接口,爲ErrorMessage提供了默認實現

RequireFieldValidator,RegexFiledValidator,EmailValidator,PhoneValidator和RangeValidator都是具體的數據驗證器,分別用於驗證非空數據,正則表達式數據,Email數據,電話號碼數據, 範圍數據,是框架爲調用者提供的常規數據驗證器.

       如果框架提供的驗證器類不能滿足要求,完全可以定義自己的數據驗證器類,在此給出一個驗證數據必須爲指定長度的示例

publicclassLengthValidator:AbstractValidator

{

        /// <summary>

        /// 最下長度

        /// </summary>

        privateint mMinLength;

        /// <summary>

        /// 最大長度

        /// </summary>

        privateint mMaxLength;

 

        /// <summary>        

        /// </summary>

        /// <param name="minLen">長度下限</param>

        /// <param name="maxLen">長度上限</param>

        /// <param name="errMsg">驗證未通過時錯誤提示信息</param>

        public LengthValidator(int minLen, int maxLen, string errMsg)

            :base(errMsg)

        {

            if (minLen < 0)

            {

                thrownewArgumentOutOfRangeException("字段長度不能爲負");

            }

            if (minLen > maxLen)

            {

                thrownewArgumentException("最大長度不能小於最下長度");

            }

            mMinLength = minLen;

            mMaxLength = maxLen;

        }

 

        /// <summary>

        /// 驗證控件內容是否在指定長度範圍內

        /// </summary>

        /// <param name="controlToValidate">待驗證控件</param>

        /// <returns>如果在範圍內,返回true;否則返回false</returns>

        publicoverridebool IsValid(Control controlToValidate)

        {

            if ((controlToValidate.Text.Length >= mMinLength) &&

                (controlToValidate.Text.Length <= mMaxLength))

            {

                returntrue;

            }

            returnfalse;

        }

    }

如果對姓名文本框(txtName)應用以下規則驗證:1.姓名不能爲空2.姓名必須是2-4個字符,則代碼大致如下

Void FormLoading(…) {

     aFormValidate. SetupValidatorForControl(txtName,

new RequireFieldValidate(),

new LengthValidate(2,4,”姓名必須是2-4個字”));

}

該驗證框架已經在筆者的多個項目中進行應用,爲項目開發節省了大量事件,讓開發人員完全從重複的數據驗證代碼中解放出來;而且,實際的使用過程也怎麼該框架具有良好的擴展性,可以自己定義驗證器來實現業務規則的驗證;同時還具有很好的非侵入性,即框架基本不會對已有代碼產生不良影響.

進一步的研究

通過AOP或者.net Attribute實現聲明性的數據驗證可以進一步減少程序代碼量

框架源代碼
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.ComponentModel;

namespace StevenZhang.FormValidateFrame
{
    /// <summary>
    /// 窗體驗證器,用於對窗體上的所有具有輸入焦點的控件進行驗證
    /// </summary>
    public class FormValidator
    {
        /// <summary>
        /// 要驗證的窗體
        /// </summary>
        private Form mFormToValid;
        /// <summary>
        /// 驗證錯誤提示控件
        /// </summary>
        private ErrorProvider mProvider;
        /// <summary>
        /// 待驗證控件數組
        /// </summary>
        private List<Control> mControlsToValidate = 
            new List<Control> ();
        /// <summary>
        /// 是否啓動驗證
        /// </summary>
        private bool mEnableValidate = true;
        /// <summary>
        /// 創建一個窗體驗證器
        /// </summary>
        /// <param name="frmToValid">要驗證的窗體</param>
        /// <param name="errProvider">驗證所用的errProvider</param>
        public FormValidator(Form frmToValid,
            ErrorProvider errProvider)
        {
            mFormToValid = frmToValid;
            mProvider = errProvider;
        }

private bool isValid=true;

public bool IsValid

{

get

{

 retuValidAll()
rn isValid;

}

}
        /// <summary>
        /// 驗證窗體的所有控件
        /// </summary>
        public void  ValidAll()
        {

           isValid=true;
            foreach (Control control in mControlsToValidate)
            {
                control.Focus();
                mFormToValid.Validate();
            }           
        }
        /// <summary>
        /// 如果爲true,表示驗證啓動;如果爲false,表示驗證沒有啓動
        /// </summary>
        public bool EnableValidate
        {
            get
            {
                return mEnableValidate;
            }
            set
            {
                if (value == mEnableValidate)
                    return;
                else
                {
                    mEnableValidate = value;
                    foreach (Control control in mControlsToValidate)
                    {
                        control.CausesValidation = mEnableValidate;
                    }
                }
            }
        }
        /// <summary>
        /// 爲窗體的所有需要驗證的控件設置驗證規則
        /// </summary>
        /// <param name="controlToValidate">要驗證的控件</param>
        /// <param name="rules">驗證規則</param>
        public void SetControlValitors(Control controlToValidate,
            params IValidator[] validators)
        {
            //判斷要驗證的控件是否已經存在與待驗證控件數組中
            
            if(!mControlsToValidate.Contains(controlToValidate))            
                mControlsToValidate.Add(controlToValidate);

            controlToValidate.Validating += delegate(Object sender,
                CancelEventArgs e)
            {
                foreach (IValidator validator in validators)
                {
                    if (!validator.IsValid(controlToValidate))
                    {
                        e.Cancel = true;
                        mProvider.SetError(controlToValidate,
                            validator.ErrorMessage);

                    isValid=false;
                        return;
                    }
                    else
                    {
                        mProvider.SetError(controlToValidate, "");
                    }
                }
            };
                
        }       
    }
    
}



using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;

namespace StevenZhang.FormValidateFrame
{
    public interface  IValidator
    {
        /// <summary>
        /// 通過該方法對控件進行驗證,如果通過驗證,返回true,否則返回false
        /// </summary>
        /// <param name="controlToValid">待驗證的控件</param>
        /// <returns>如果控件通過該驗證,返回true;否則返回false</returns>
        bool IsValid(Control controlToValid);
        /// <summary>
        /// 驗證沒有通過的時候,需要顯示的錯誤提示信息
        /// </summary>
        string ErrorMessage
        {
            get;
            set;
        }
    }
}



using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;

namespace StevenZhang.FormValidateFrame
{
    public class LengthValidator:AbstractValidator
    {
        /// <summary>
        /// 最下長度
        /// </summary>
        private int mMinLength;
        /// <summary>
        /// 最大長度
        /// </summary>
        private int mMaxLength;

        /// <summary>        
        /// </summary>
        /// <param name="minLen">長度下限</param>
        /// <param name="maxLen">長度上限</param>
        /// <param name="errMsg">驗證未通過時錯誤提示信息</param>
        public LengthValidator(int minLen, int maxLen, string errMsg)
            :base(errMsg)
        {
            if (minLen < 0)
            {
                throw new ArgumentOutOfRangeException("字段長度不能爲負");
            }
            if (minLen > maxLen)
            {
                throw new ArgumentException("最大長度不能小於最下長度");
            }
            mMinLength = minLen;
            mMaxLength = maxLen;
        }

        /// <summary>
        /// 驗證控件內容是否在指定長度範圍內
        /// </summary>
        /// <param name="controlToValidate">待驗證控件</param>
        /// <returns>如果在範圍內,返回true;否則返回false</returns>
        public override bool IsValid(Control controlToValidate)
        {
            if ((controlToValidate.Text.Length >= mMinLength) &&
                (controlToValidate.Text.Length <= mMaxLength))
            {
                return true;
            }
            return false;
        }
    }
}


using System;
using System.Windows.Forms;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;

namespace StevenZhang.FormValidateFrame
{
    /// <summary>
    /// 正則表達式驗證器
    /// </summary>
    public class RegexValidator:AbstractValidator 
    {
        /// <summary>
        /// 正則表達式對象
        /// </summary>
        private Regex mRegex;

        /// <summary> 
        /// </summary> 
        /// <param name="pattern">正則表達式</param>
        public RegexValidator(string pattern ,string errMsg):base(errMsg)
        {
            mRegex = new Regex(pattern);           
        }

        #region IValidator部分
        public override bool IsValid(Control controlToValidate)
        {
            return mRegex.IsMatch(controlToValidate.Text);
        }        
        #endregion
    }
}



using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;

namespace StevenZhang.FormValidateFrame
{
    /// <summary>
    /// 非空字段驗證器
    /// </summary>
    public class RequiredFieldValidator:AbstractValidator
    {
        private const string DefaultErrMsg = "該內容不能爲空";
        /// <summary>       
        /// </summary>        
        /// <param name="errMsg">驗證不通過時的錯誤信息</param>
        public RequiredFieldValidator(string errMsg)
            : base(errMsg)
        {                      
        }
        public RequiredFieldValidator():base(DefaultErrMsg)
        {           
        }

        #region IValidator部分
        /// <summary>
        ///驗證內容必須不爲空
        /// </summary>
        /// <param name="content">要驗證的內容</param>
        /// <returns></returns>
        public override bool IsValid(Control controlToValidate)
        {
            string content = controlToValidate.Text;

            if ((content == null) ||
                (content.Trim().Length == 0))
            {
                return false;
            }
            return true;
        }      
        #endregion

    }
}
abstract class AbstractValidator:IValidator
{
protected string _errorMessage;

public AbstractValidator(string errorMsg)
{
_errorMessage = errorMsg;
}
#region IValidator 成員
/// <summary>
/// 錯誤信息
/// </summary>
public string ErrorMessage
{
get
{
return _errorMessage;
}
set
{
_errorMessage = value;
}
}

public abstract bool IsValid(Control controlToValidate);

#endregion
}
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章