久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

Windows Forms中通過(guò)自定義組件實(shí)現(xiàn)統(tǒng)一的數(shù)據(jù)驗(yàn)證(一) - 一個(gè)程序員的自省 ...

 秋水風(fēng)帆 2010-12-22

Windows Forms中通過(guò)自定義組件實(shí)現(xiàn)統(tǒng)一的數(shù)據(jù)驗(yàn)證(一)

2007-04-18 21:30 by Anders Cui, 5446 visits, 網(wǎng)摘, 收藏, 編輯

摘要

一直對(duì)WinForm中沒(méi)有像WebForm中那樣的驗(yàn)證控件耿耿于懷,,這幾天準(zhǔn)備開(kāi)發(fā)一套類(lèi)似的控件,。在網(wǎng)上找到大牛Michael Weinhardt的一個(gè)系列文章,寫(xiě)得非常棒,,所以基本上按他的思路下來(lái)的,。

在獲取用戶(hù)輸入及后續(xù)的處理過(guò)程中,數(shù)據(jù)校驗(yàn)是關(guān)鍵的一步,。本文將對(duì)Windows Forms中的校驗(yàn)機(jī)制進(jìn)行探討,,分析如何通過(guò)開(kāi)發(fā)自定義驗(yàn)證組件來(lái)提供更為高效的驗(yàn)證體驗(yàn)(類(lèi)似于ASP.NET中的驗(yàn)證控件)。

Windows Forms 驗(yàn)證機(jī)制介紹

簡(jiǎn)單地說(shuō),,驗(yàn)證是對(duì)數(shù)據(jù)進(jìn)行處理前確保其完整和正確的過(guò)程,。驗(yàn)證可以實(shí)現(xiàn)在數(shù)據(jù)層和業(yè)務(wù)規(guī)則層,而應(yīng)當(dāng)在表現(xiàn)層進(jìn)行前端的”保護(hù)”,。開(kāi)發(fā)人員通常在UI中為用戶(hù)提供友好的,、可交互的驗(yàn)證體驗(yàn),而要避免在N層應(yīng)用程序中進(jìn)行不必要的網(wǎng)絡(luò)間往返驗(yàn)證,。驗(yàn)證包含數(shù)據(jù)類(lèi)型,、范圍或業(yè)務(wù)規(guī)則等類(lèi)型,看下面這個(gè)簡(jiǎn)單的例子:

<!--[if !vml]-->
<!--[endif]-->

這個(gè)窗體中需要進(jìn)行下列驗(yàn)證:

  • Name,,Date of Birth和Phone Number為必填項(xiàng)
  • Date of Birth必須為正確的日期值
  • Phone Number必須為正確的格式
  • 新添加的雇員必須年滿(mǎn)18歲(杜絕童工)

要完成這些驗(yàn)證需要一個(gè)合適的機(jī)制,,Windows Forms已經(jīng)提供了一種,內(nèi)置在每個(gè)控件中,。要使控件支持驗(yàn)證,,須將它的CausesValidation 屬性設(shè)置為true,這也是所有控件的默認(rèn)值。如果控件的CausesValidation 屬性設(shè)置為true,,那么在它將焦點(diǎn)轉(zhuǎn)移到另一個(gè)控件(并且它的CausesValidation也為true)時(shí)會(huì)觸發(fā)Validating 事件,。因此,我們可以處理控件的Validating事件,,在這里實(shí)現(xiàn)驗(yàn)證邏輯,,像下面這樣:

private void txtName_Validating(object sender, CancelEventArgs e)
{
    
if (txtName.Text.Trim().Length == 0)
    {
        e.Cancel 
= true;
        
return;
    }
}

Validating 事件提供了CancelEventArgs 類(lèi)型的參數(shù),它的Cancel屬性使我們可以指定控件的值是否有效,。如果Cancel為true(即是無(wú)效的),,焦點(diǎn)仍然停留在無(wú)效的控件中;如果Cancel值為false(即通過(guò)了驗(yàn)證),,則會(huì)觸發(fā)Validated事件,,焦點(diǎn)也會(huì)轉(zhuǎn)移到新的控件。

現(xiàn)在,,責(zé)任落到了我們開(kāi)發(fā)人員這邊,,要以可視化的方式通知用戶(hù)數(shù)據(jù)是否有效,也許你想到的是狀態(tài)欄,,這種方式存在兩個(gè)問(wèn)題:

 

  • 狀態(tài)欄只能每次顯式一條錯(cuò)誤信息,,即使窗體包含多個(gè)無(wú)效的控件輸入;
  • 狀態(tài)欄離輸入控件”很遠(yuǎn)”,,很難確切指明哪個(gè)控件出現(xiàn)了錯(cuò)誤,。
據(jù)傳聞,微軟曾做過(guò)這么一個(gè)可用性研究:人們坐在椅子上運(yùn)行一個(gè)程序,,狀態(tài)欄給出一個(gè)通知信息叫他們往椅子底下看,,這樣就可以得到50美元獎(jiǎng)金。但在測(cè)試期間,,竟沒(méi)有任何人能拿走這50美元,!

 

此時(shí),ErrorProvider組件是更好的選擇:


ErrorProvider組件的用法非常簡(jiǎn)單,,此處不再贅述,,Validating事件的代碼如下:

if (txtName.Text.Trim().Length == 0)
{
    errorProvider1.SetError(txtName, 
"Name is required.");
    e.Cancel 
= true;
    
return;
}

errorProvider1.SetError(txtName, 
string.Empty);

CausesValidationValidatingErrorProvider提供了控件級(jí)驗(yàn)證的基礎(chǔ)機(jī)制,,我們可以用它們對(duì)控件逐一進(jìn)行驗(yàn)證,。

窗體級(jí)驗(yàn)證

ValidatingErrorProvider這對(duì)組合是一個(gè)不錯(cuò)的解決方案,可以在用戶(hù)輸入數(shù)據(jù)的時(shí)候進(jìn)行驗(yàn)證,。不幸的是,這種方法可能會(huì)使得我們無(wú)法進(jìn)行窗體級(jí)的驗(yàn)證,,而這在用戶(hù)點(diǎn)擊OK按鈕提交數(shù)據(jù)時(shí)顯然是必要的,,因?yàn)橛脩?hù)在點(diǎn)擊OK按鈕前,有些控件可能未曾獲得過(guò)焦點(diǎn),它們的控件級(jí)驗(yàn)證代碼也就不起作用了,。先看看窗體級(jí)驗(yàn)證的代碼:

foreach (Control ctrl in this.Controls)
{
    ctrl.Focus();

    
if (!Validate())
    {
        
this.DialogResult = DialogResult.None;
        
return;
    }
}

但Cancel按鈕就不需要實(shí)現(xiàn)窗體級(jí)的驗(yàn)證了,,它的工作往往是簡(jiǎn)單地將窗體關(guān)閉。但是現(xiàn)在,,如果當(dāng)前擁有焦點(diǎn)的控件數(shù)據(jù)是無(wú)效的,,Cancel按鈕將不能點(diǎn)擊,因?yàn)镃ancel按鈕的CausesValidation屬性默認(rèn)為true,,焦點(diǎn)會(huì)一直停留在無(wú)效的控件上,。我們只要將Cancel按鈕的CausesValidation屬性設(shè)置為false就好了。 

注意:這里的窗體應(yīng)當(dāng)是模式窗體,,否則即使CausesValidation屬性設(shè)置為false,,也不能點(diǎn)擊。

至此,,使用數(shù)十行代碼,,我們的AddEmployee窗體就可以支持基本的驗(yàn)證了。

編程式驗(yàn)證 vs. 聲明式驗(yàn)證

從生產(chǎn)力的角度來(lái)看,,上面的解決方案有一個(gè)根本的問(wèn)題:如果一個(gè)程序包含多個(gè)窗體,,而每個(gè)窗體又包含多個(gè)控件,那么將需要大量的用于驗(yàn)證的代碼,。這些代碼增大了UI的復(fù)雜性,,使得程序難以維護(hù),顯式是應(yīng)當(dāng)避免的,。一種方法是將那些通用的驗(yàn)證邏輯抽象為可重用的類(lèi)型,。有了這樣的類(lèi)型,還僅僅是第一步,,它仍需要編寫(xiě)代碼,。{TODO}我們需要這樣的解決方案:它具有Windows Forms UI的特點(diǎn),因此Windows Forms組件或控件是我們不錯(cuò)的選擇,。以這種方式封裝后,,開(kāi)發(fā)人員的工作就變成從工具箱上拖一個(gè)組件或控件放到窗體上,通過(guò)諸如屬性瀏覽器(Property Browser)這樣的設(shè)計(jì)期特性來(lái)配置它,,然后讓W(xué)indows Forms設(shè)計(jì)器幫我們將這些配置轉(zhuǎn)換為代碼,,這些代碼會(huì)出現(xiàn)在InitializeComponent方法中。這樣原來(lái)的編程式(programmatic)體驗(yàn)變成了聲明式(declarative)體驗(yàn),,而后者往往意味著高效,。

添加設(shè)計(jì)期支持

第一步是添加設(shè)計(jì)期的支持,如果我們的實(shí)現(xiàn)不需要UI支持,,可以從三種設(shè)計(jì)期組件繼承:System.ComponentModel.Component,, System.Windows.Forms.ControlSystem.Windows.Forms.UserControl. Component,,否則可以繼承ControlUserControlControlUserControl的不同之處在于其呈現(xiàn)的方式,,前者需要編寫(xiě)代碼來(lái)呈現(xiàn)它,,而后者則通過(guò)其它控件或組件來(lái)呈現(xiàn)它。我們?cè)谇懊媸褂玫尿?yàn)證代碼沒(méi)有繪制任何內(nèi)容,,而是借助于ErrorProvider來(lái)提示用戶(hù),。因此,Component是我們最合適的選擇,。

Imitation Is the Sincerest Form of Flattery

下一步是要確定我們需要哪些種類(lèi)的驗(yàn)證組件,,可以參考一下ASP.NET中驗(yàn)證控件的實(shí)現(xiàn)機(jī)制。這樣能保持一定的一致性,,而且也不需要”重新發(fā)明輪子”了,。這樣那些ASP.NET的開(kāi)發(fā)人員也更容易上手。ASP.NET現(xiàn)在提供了如下的驗(yàn)證控件:

驗(yàn)證控件

描述

RequiredFieldValidator

計(jì)算輸入控件的值以確保用戶(hù)輸入值,。

RegularExpressionValidator

計(jì)算輸入控件的值,,以確定該值是否與某個(gè)正則表達(dá)式所定義的模式相匹配。

CompareValidator

將由用戶(hù)輸入到輸入控件的值與輸入到其他輸入控件的值或常數(shù)值進(jìn)行比較,。

RangeValidator

檢查輸入控件的值是否在指定的值范圍內(nèi),。

CustomValidator

對(duì)輸入控件執(zhí)行用戶(hù)定義的驗(yàn)證。

同時(shí)我們還要考慮可擴(kuò)展性,,開(kāi)發(fā)人員在必要的時(shí)候可以較為容易地開(kāi)發(fā)自定義的驗(yàn)證組件,。最后,這個(gè)實(shí)現(xiàn)應(yīng)當(dāng)利用Windows Forms中已有的驗(yàn)證機(jī)制(前面提及的部分),。

引入RequiredFieldValidator

有了上面的設(shè)計(jì)思路,,現(xiàn)在要來(lái)點(diǎn)真的了。讓我們從最簡(jiǎn)單的驗(yàn)證情形開(kāi)始:RequiredFieldValidator,。

建立一個(gè)Component類(lèi),,名稱(chēng)為RequiredFieldValidator,其接口應(yīng)當(dāng)與ASP.NET中的對(duì)應(yīng)類(lèi)相同:

public partial class RequiredFieldValidator : Component
{
    
string ControlToValidate { getset;}
    
string ErrorMessage { getset;}
    
string InitialValue { getset;}
    
bool IsValid { getset;}
    
void Validate();
}

下面是每個(gè)成員的含義:

成員

描述

ControlToValidate

指定要驗(yàn)證的控件

ErrorMessage

控件未通過(guò)驗(yàn)證時(shí)顯式的信息,。

InitialValue

某些情況下,,控件的默認(rèn)值用作提示,如”請(qǐng)選擇種類(lèi)”,,這時(shí)必填項(xiàng)意味著必須與默認(rèn)值不同,。此時(shí)用InitialValue。

IsValid

在調(diào)用Validate方法后報(bào)告控件的數(shù)據(jù)是否有效,,默認(rèn)為true,。

Validate

驗(yàn)證指定控件的值,并設(shè)置IsValid,。


在ASP.NET中,,ControlToValidate是字符串類(lèi)型的,,這種間接的做法在基于請(qǐng)求、無(wú)狀態(tài)的Web應(yīng)用程序中是必要的,。但在Windows Forms中我們則不必這么做,我們可以直接引用控件,。同時(shí),,我們要在內(nèi)部使用ErrorProvider組件,所以為其添加一個(gè)Icon屬性:

public partial class RequiredFieldValidator : Component
{
    …
    Control ControlToValidate { 
getset;}
    Icon Icon { 
getset;}
    …
}

好,,來(lái)看看具體的實(shí)現(xiàn)代碼:

public partial class RequiredFieldValidator : Component
    
{
        
Private Fields

        
Constructors

        
Public Properties

        
public void Validate()
        
{
            
if (controlToValidate == null)
            
{
                isValid 
= true;
                
return;
            }


            
string controlValue = controlToValidate.Text.Trim();
            
string _initValue;
            
if (initialValue == null)
            
{
                _initValue 
= string.Empty;
            }

            
else
            
{
                _initValue 
= initialValue.Trim();
            }

            isValid 
= (controlValue != _initValue);

            
if (isValid)
            
{
                errorProvider.SetError(controlToValidate, 
string.Empty);
            }

            
else
            
{
                errorProvider.SetError(controlToValidate, errorMessage);
            }

        }


        
private void controlToValidate_Validating(object sender, CancelEventArgs e)
        
{
            Validate();
        }

    }

 

這種實(shí)現(xiàn)的關(guān)鍵在于如何掛接ControlValidate控件的Validating事件,,這種做法與前面的控件級(jí)驗(yàn)證相一致,還有一個(gè)額外的好處,,這里的ControlToValidate_Validating方法中,,沒(méi)有設(shè)置CancelEventArgs參數(shù)的Cancel屬性,這樣就不會(huì)把用戶(hù)困在一個(gè)控件中,。

組件的驗(yàn)證功能已經(jīng)實(shí)現(xiàn)了,,同時(shí)還為其添加了設(shè)計(jì)期支持。最終實(shí)現(xiàn)還提供了其它一些設(shè)計(jì)期特性:

  • <!--[if !supportLists]-->指定了在屬性瀏覽器中設(shè)置ControlToValidate時(shí)可以選擇的控件種類(lèi),;
  • 在屬性瀏覽器中隱藏了IsValid屬性,,因?yàn)樗沁\(yùn)行時(shí)的屬性。

編譯,,然后將組件添加到工具箱,。

讓我們回到前面的AddEmployee窗體,現(xiàn)在不再需要處理Validating事件了,,只要拖3個(gè)組件到窗體,,然后為它們?cè)O(shè)置屬性。

<!--[if !vml]-->
<!--[endif]-->

其中Phone Number域的驗(yàn)證組件的InitialValue為”Your number here.”,。怎么樣,,是不是很high?

BaseValidator:分而治之

實(shí)現(xiàn)了RequiredFieldValidator后,,其它類(lèi)型的驗(yàn)證組件應(yīng)當(dāng)比較容易實(shí)現(xiàn)了,。先別急,可沒(méi)你想的那么簡(jiǎn)單,。RequiredFieldValidator類(lèi)把特定的”必填”邏輯和其它對(duì)每個(gè)驗(yàn)證組件都適用的通用邏輯耦合在一起了,。這種情況下,應(yīng)當(dāng)把RequiredFieldValidator分解為兩個(gè)類(lèi)型:BaseValidator和減肥后的RequiredFieldValidator,。

abstract class BaseValidator : Component 
{
    
    
void Validate() 
    {
        
        _isValid 
= EvaluateIsValid();
        
    }
    
protected abstract bool EvaluateIsValid();
}

這樣定義的效果是,,BaseValidator必須通過(guò)繼承后才能使用,而EvaluateIsValid則必須實(shí)現(xiàn),。Validate方法通過(guò)EvaluateIsValid方法來(lái)設(shè)置IsValid,。這種技術(shù)也應(yīng)用在了ASP.NET的驗(yàn)證控件上,。

BaseValidator實(shí)現(xiàn)后,需要對(duì)RequiredFieldValidator進(jìn)行重構(gòu):

[ToolboxBitmap(typeof(RequiredFieldValidator), "RequiredFieldValidator.ico")]
class RequiredFieldValidator : BaseValidator 
{
    
string InitialValue  {}
    
protected override bool EvaluateIsValid() 
    {
        
string controlValue = ControlToValidate.Text.Trim();
        
string initialValue;
        
if( _initialValue == null ) initialValue = "";
        
else initialValue = _initialValue.Trim();
        
return (controlValue != initialValue);
    }
}

更進(jìn)一步,,實(shí)現(xiàn)其它驗(yàn)證組件
 

通過(guò)使用基類(lèi)和派生類(lèi)將通用邏輯和特定邏輯分離后,,我們可以把注意力集中在特定的驗(yàn)證邏輯,這在RequiredFieldValidator中效果不錯(cuò),。下面會(huì)看到,,對(duì)于其它類(lèi)型的驗(yàn)證組件同樣很好,它們是:

  • <!--[if !supportLists]-->RegularExpressionValidator
  • CustomValidator
  • CompareValidator
  • RangeValidator

現(xiàn)在把它們一一實(shí)現(xiàn),。

RegularExpressionValidator

正則表達(dá)式是一種強(qiáng)大的文本模式匹配技術(shù),。如果文本域需要一定的模式,正則表達(dá)式無(wú)疑是很好的選擇,。

using System.Text.RegularExpressions;

[ToolboxBitmap(
typeof(RegularExpressionValidator), "RegularExpressionValidator.ico")]
class RegularExpressionValidator : BaseValidator 
{
    
    
string ValidationExpression {}
    
protected override bool EvaluateIsValid() 
    {
        
// Don't validate if empty
        if( ControlToValidate.Text.Trim() == "" ) return true;
        
// Successful if match matches the entire text of ControlToValidate
        string field = ControlToValidate.Text.Trim();
        
return Regex.IsMatch(field, _validationExpression.Trim());  
    }
}

在設(shè)計(jì)時(shí),,開(kāi)發(fā)人員可以通過(guò)屬性瀏覽器提供用于驗(yàn)證的正則表達(dá)式。

CustomValidator

人生在世,,不如意者十有八九,。我們定義的驗(yàn)證組件不可能解決所有問(wèn)題,尤其是面對(duì)復(fù)雜的業(yè)務(wù)規(guī)則的時(shí)候,。這時(shí)只能編寫(xiě)自定義代碼,,CustomValidator 允許我們編寫(xiě)這些自定義代碼,同時(shí)仍能與其它的驗(yàn)證組件保持一致,,這在窗體級(jí)的統(tǒng)一驗(yàn)證過(guò)程中很重要,。CustomValidator 提供了Validating事件和ValidatingCancelEventArgs:

處理CustomValidator的Validating事件時(shí),只需在屬性瀏覽器中雙擊:

然后,,只需添加合適的驗(yàn)證邏輯,,以確保新增的雇員不小于18歲:

private void customValidator1_Validating(object sender, CustomValidator.ValidatingCancelEventArgs e)
{
    DateTime birth;
    
bool isDate = DateTime.TryParse(txtBirth.Text, out birth);
    
if (isDate)
    {
        DateTime legal 
= DateTime.Now.AddYears(-18);
        e.Valid 
= (birth <= legal);
    }
    
else
    {
        e.Valid 
= false;
    }
}

 

如果小于18歲,就會(huì)提示用戶(hù):

 

BaseCompareValidator

到目前為止,,我們的組件只能處理單個(gè)文本域的值,。但在某些情況下,驗(yàn)證過(guò)程可能涉及多個(gè)文本域或值,,比如確保文本域的值在兩個(gè)值之間(RangeValidator),;或比較兩個(gè)文本域的值是否相等(CompareValidator)。不管哪種情況,,我們都需要考慮類(lèi)型檢查,、轉(zhuǎn)換和比較等過(guò)程。這個(gè)功能應(yīng)當(dāng)封裝在一個(gè)新的類(lèi)型中:BaseCompareValidator,,而RangeValidator和CompareValidator則繼承自它,。

ValidationDataType是一個(gè)自定義枚舉類(lèi)型,在何種數(shù)據(jù)類(lèi)型下進(jìn)行比較驗(yàn)證,。

RangeValidator

如果需要確??丶妮斎胫翟谥付ǖ姆秶鷥?nèi),,RangeValidator 可以滿(mǎn)足需要。它需要開(kāi)發(fā)人員指定最大值和最小值,,還有輸入值的數(shù)據(jù)類(lèi)型,。

<!--[if !vml]-->
<!--[endif]-->

CompareValidator

最后來(lái)看看CompareValidator,它用來(lái)進(jìn)行控件的等值測(cè)試,,可以與另一個(gè)控件的值或者指定的值進(jìn)行比較,。Operator屬性指定了比較操作的類(lèi)型,ControlToCompare和 ValueToCompare則指定了要比較的控件和指定值,。如果Operator屬性為DataTypeCheck,則還可以判斷控件的值是否為指定類(lèi)型,。

<!--[if !vml]-->
<!--[endif]-->

完整的自定義驗(yàn)證組件結(jié)構(gòu)




我們身在何處

首先我們對(duì)Windows Forms中的校驗(yàn)機(jī)制進(jìn)行了探討,,然后將這些驗(yàn)證邏輯封裝到了幾個(gè)支持設(shè)計(jì)時(shí)操作的組件,通過(guò)開(kāi)發(fā)自定義驗(yàn)證組件來(lái)提供更為高效的驗(yàn)證體驗(yàn)(類(lèi)似于ASP.NET中的驗(yàn)證控件),。但目前還僅限于控件級(jí)的驗(yàn)證,。下一篇文章中將討論如何進(jìn)行窗體級(jí)的驗(yàn)證,屆時(shí)ValidationSummary組件也會(huì)閃亮登場(chǎng),。

示例代碼下載:CustomValidatorSample.rar

參考:
1. Extending Windows Forms with a Custom Validation Component Library. By Michael Weinhardt
2. Windows Forms Programming in C#. By Chris Sells
作者:Anders Cui
出處:http://anderslly.cnblogs.com
本文版權(quán)歸作者和博客園共有,,歡迎轉(zhuǎn)載,但未經(jīng)作者同意必須保留此段聲明,,且在文章頁(yè)面明顯位置給出原文連接,,否則保留追究法律責(zé)任的權(quán)利。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,,所有內(nèi)容均由用戶(hù)發(fā)布,,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購(gòu)買(mǎi)等信息,,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請(qǐng)點(diǎn)擊一鍵舉報(bào),。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶(hù) 評(píng)論公約

    類(lèi)似文章 更多