九色国产,午夜在线视频,新黄色网址,九九色综合,天天做夜夜做久久做狠狠,天天躁夜夜躁狠狠躁2021a,久久不卡一区二区三区

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

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

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

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

摘要

一直對WinForm中沒有像WebForm中那樣的驗證控件耿耿于懷,這幾天準備開發(fā)一套類似的控件。在網(wǎng)上找到大牛Michael Weinhardt的一個系列文章,寫得非常棒,所以基本上按他的思路下來的。

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

Windows Forms 驗證機制介紹

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

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


<!--[endif]-->

這個窗體中需要進行下列驗證:

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

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

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

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

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

 

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

 

此時,ErrorProvider組件是更好的選擇:


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

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

errorProvider1.SetError(txtName, 
string.Empty);

CausesValidation、ValidatingErrorProvider提供了控件級驗證的基礎機制,我們可以用它們對控件逐一進行驗證。

窗體級驗證

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

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

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

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

注意:這里的窗體應當是模式窗體,否則即使CausesValidation屬性設置為false,也不能點擊。

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

編程式驗證 vs. 聲明式驗證

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

添加設計期支持

第一步是添加設計期的支持,如果我們的實現(xiàn)不需要UI支持,可以從三種設計期組件繼承:System.ComponentModel.Component, System.Windows.Forms.ControlSystem.Windows.Forms.UserControl. Component,否則可以繼承ControlUserControl。ControlUserControl的不同之處在于其呈現(xiàn)的方式,前者需要編寫代碼來呈現(xiàn)它,而后者則通過其它控件或組件來呈現(xiàn)它。我們在前面使用的驗證代碼沒有繪制任何內容,而是借助于ErrorProvider來提示用戶。因此,Component是我們最合適的選擇。

Imitation Is the Sincerest Form of Flattery

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

驗證控件

描述

RequiredFieldValidator

計算輸入控件的值以確保用戶輸入值。

RegularExpressionValidator

計算輸入控件的值,以確定該值是否與某個正則表達式所定義的模式相匹配。

CompareValidator

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

RangeValidator

檢查輸入控件的值是否在指定的值范圍內。

CustomValidator

對輸入控件執(zhí)行用戶定義的驗證。

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

引入RequiredFieldValidator

有了上面的設計思路,現(xiàn)在要來點真的了。讓我們從最簡單的驗證情形開始:RequiredFieldValidator。

建立一個Component類,名稱為RequiredFieldValidator,其接口應當與ASP.NET中的對應類相同:

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

下面是每個成員的含義:

成員

描述

ControlToValidate

指定要驗證的控件

ErrorMessage

控件未通過驗證時顯式的信息。

InitialValue

某些情況下,控件的默認值用作提示,如”請選擇種類”,這時必填項意味著必須與默認值不同。此時用InitialValue。

IsValid

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

Validate

驗證指定控件的值,并設置IsValid。


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

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

好,來看看具體的實現(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();
        }

    }

 

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

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

  • <!--[if !supportLists]-->指定了在屬性瀏覽器中設置ControlToValidate時可以選擇的控件種類;
  • 在屬性瀏覽器中隱藏了IsValid屬性,因為它是運行時的屬性。

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

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

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


<!--[endif]-->

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

BaseValidator:分而治之

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

abstract class BaseValidator : Component 
{
    

    
void Validate() 
    {
        

        _isValid 
= EvaluateIsValid();
        

    }
    
protected abstract bool EvaluateIsValid();
}

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

BaseValidator實現(xiàn)后,需要對RequiredFieldValidator進行重構:

[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);
    }
}

更進一步,實現(xiàn)其它驗證組件
 

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

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

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

RegularExpressionValidator

正則表達式是一種強大的文本模式匹配技術。如果文本域需要一定的模式,正則表達式無疑是很好的選擇。

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());  
    }
}

在設計時,開發(fā)人員可以通過屬性瀏覽器提供用于驗證的正則表達式。

CustomValidator

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

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

然后,只需添加合適的驗證邏輯,以確保新增的雇員不小于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歲,就會提示用戶:

 

BaseCompareValidator

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

ValidationDataType是一個自定義枚舉類型,在何種數(shù)據(jù)類型下進行比較驗證。

RangeValidator

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

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


<!--[endif]-->

CompareValidator

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

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


<!--[endif]-->

完整的自定義驗證組件結構




我們身在何處

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

示例代碼下載: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
本文版權歸作者和博客園共有,歡迎轉載,但未經(jīng)作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。
本站僅提供存儲服務,所有內容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權內容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
彼岸花開
ASP.NET六種驗證控件
ASP.NET驗證控件[轉]
.Net驗證控件與自定義驗證合作無間
RequiredFieldValidator控件為上文舉例
創(chuàng)建客戶端組件和控件
更多類似文章 >>
生活服務
熱點新聞
分享 收藏 導長圖 關注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服