Mexx 的个人资料我們走吧 我們回去照片日志列表更多 工具 帮助
10月5日

Validation of User Controls in ASP.Net

常用的控制項組合,我們會想把它包在一起,日後方便重複使用。
寫 Custom Control 太麻煩了,通常會用 User Control 快速達到我們的需要。
所以就會衍生出這個問題:如何驗證 User Control 的值?例如 User Control 裡頭的某個 TextBox?



首先談兩個觀念
第一是:User Control 包了若干個 Server Control ( or Html ) 之後,外界對它是一無所知的只知道它是個 "UserControl" 類別˙。你必須為它設計一些「屬性」,把該 User Control 的特性給釋出來讓外界知道(讓 aspx 知道)。例如該 User Control 內含一個 TextBox,你若要在 aspx 可以存取該 TextBox 的值,可以設計一個屬性如下:

public string Text
{
    get { return this.TextBox1.Text; }
    set { this.TextBox1.Text = value; }
}

相關的介面:ITextControl、IEditableTextControl,只要繼承了他們,就一定要有 Text 屬性。

第二個觀念是:"UserControl" 類別繼承了 INamingContainer,所以上面 TextBox1 Render 到前端後,不會仍然是 id="TextBox1"


以下還有一些常見問題:
  • Q:如何在「屬性視窗」可以看到該屬性?
    R( Reference ):查詢 "Browsable"
    PS:就算屬性視窗看不到,還是可以直接用 Tag  的方式指定屬性值,也可以在 vb/cs 檔案中指定。
    .
  • Q:如何讓屬性支援資料繫結 ( DataBinding )?
    R:查詢 "Bindable"
    PS:Visual Studio 開出來的 databinding dialog 中,仍然看不到該屬性,這是 VS 的 Bug,但仍然可以直接以 Tag 的方式設定 databinding。例如:Text='<%# Bind("StartDate", "{0:yyyy/MM/dd}") %>'
    .
  • Q:我在 User Control 裡面塞了好幾個驗證控制項,這些驗證控制項我只在乎它們會不會做輸入驗證,在設計階段希望可以隱藏,才不會拉一個 User Control 出來,就佔了 aspx 版面一大塊。
    R:試試看 PlaceHolder
    .




接下來,就進入比較主題的部分,就是如何驗證 User Control 的屬性值?有兩個做法:
  1. 在 User Control 裡面就先佈置好驗證控制項,這個比較簡單,通常適合放一些必要的驗證而已,不要阿貓阿狗什麼都拉進來,把它搞大了不是好事。例如你要做純日期輸入的 TextBox,你也許就會塞個 RangeValidator,或是利用 AjaxControlToolkit 的驗證控制項
  2. 當 User Control 拉到 aspx 頁面上之後,你要用 aspx 頁面上的驗證控制項來驗證 User Control 的值。那麼,請參考 "ValidationProperty" ...... 真的有這麼簡單嗎?
實際上還是會遇到許多問題。(有文章說還需要 "ControlValueProperty" 屬性,其實不需要,這是兩回事)







以下用 RequiredFieldValidator 舉例:
  • Q:驗證是沒有問題,但變成一定要 PostBack 之後才能得到驗證結果
    R:驗證控制項會偷偷產生一些 JavaScript 幫我們做 Client 端驗證,你若檢視原始檔,會發現有一堆驗證的 script,有沒有看到 xxxxxValidator.controltovalidate = "某某控制項ClientID" ?這就是了!它需要「正確」的 ClientID 來協助驗證。可是你會發現,這個 "
    某某控制項 ClientID" 是該 User Control 的 ClientID,而不是它裡面那個 TextBox 的 ClientID,當然就無法做 Client 端驗證了。
    A:宣告一個隱藏欄位(Hidden Field),來代表該 User Control 的值,欺騙 RequiredFieldValidator 做 Client 驗證。
    1. 在 User Control 的 Page Load 加入 Page.ClientScript.RegisterHiddenField(this.ClientID, this.Text);
    2. 設定內部 TextBox 的 onchange 事件,同時改變 Hidden Field 的值。this.dateTextBox.Attributes["onchange"] = ..........
      .
  • Q:某天你把它放到資料繫結控制項(GridView、FormView .....)的 "Template" 裡面....又發現一堆問題!你對該 User Control 的 Text 屬性設定資料繫結,進入 FormView 的編輯模式時,TextBox 明明是有值的,外面那個 RequiredFieldValidator 卻當作沒看見,submit 動作一直被它檔下來。
    R:在 User Control 的 Page Load 事件裡面,它根本還沒做資料繫結,所以 Text 值是空字串,你馬上註冊 Hidden Field,所以它的值也是空字串 ........ RequiredFieldValidator 果然不馬虎,很盡忠職守。所以註冊 Hidden Field,應該要等到確定 Bind 完資料之後,建議改到 OnPreRender 事件。

做到這邊
其實可接受了,若你已經滿意,底下的就可以忽略不看。







  • Q:但仔細比對原本 RequiredFieldValidator 的行為,你發現有些微差異:本來是 TextBox 失去焦點,或改變值就會驗證,現在卻變成要按 Button 才驗證?
    • R:可以多試試看,會發現 RequiredFieldValidator 放在 User Control 裡面,就一切正常,放在 aspx 就會有這個問題,但我前面說了,你總不能把所有驗證控制項都塞進 User Control,這太不像話了 .....
      .
    • R:拉一個頁面,用 RequiredFieldValidator 驗證一般 TextBox,然後利用 JavaScript 偵錯工具(例如Firebug、IE8內附、Visual Studio ...)查看到底 TextBox 的 onchange 事件發生後,偷偷做了什麼事情,否則為何 RequiredFieldValidator 會立刻知道?結果發現它呼叫了 ValidatorOnChange(event)?一看就知道是驗證控制項搞的鬼,我們就有樣學樣,在原本 this.dateTextBox.Attributes["onchange"] 事件中再加上 "ValidatorOnChange(event)"
      .
    • R:噩夢竟然還沒結束?RequiredFieldValidator依然沒有在 TextBox  onchange 時立刻驗證。那麼就試著偷看 ValidatorOnChange 函數到底做了什麼?想辦法在執行它之前先中斷 JavaScript(提示:debugger),然後利用單步執行一一深入。你會發現一些 JavaScript 試著取得驗證該 TextBox 的所有控制項」,是一個陣列,可是竟然只得到 User Control 內部的驗證控制項,在 aspx 上面的驗證控制項它都當作沒看見 ...... 其實這是理所當然的,因為放在 User Control 外面的 RequiredFieldValidator,它在 Client 端真正驗證的對象是我們給的隱藏欄位,而不是 User Control 裡面的 TextBox。
      .
    • R:直覺的解決方式,能否把外面的 RequiredFieldValidator ,也加到該陣列裡面?那就再找看看,到底是什麼時候把驗證控制項加入該陣列?又呼叫了什麼函數?
      .
    • R:經過追查,得知 ValidatorHookupControl 這個函數,它可以接受兩個參數:第一個是「真正被驗證的控制項的 Client 物件」,第二個是「驗證控制項的 Client 物件」,舉個例也許較容易懂:
      ValidatorHookupControl(document.getElementById('textbox1ClientId'),
      document.getElementById('validatorClientId')); 把該指令也加到 TextBox 的 onchange 事件中。這樣就可以把該驗證控制項加入到陣列裡面
      .
    • 做完以上動作後,驗證控制項就會完全恢復往日雄風了!







嚴格來說,還是有點美中不足的:
  • Q:你知道 aspx 中有哪些驗證控制項針對該 User Control 做驗證,但是該 User Control 怎麼主動得知:「有誰對我做驗證?」難不成我們每次都要手動自己 key 嗎?
    R:愈扯愈遠了................ 建議寫一個函數,或是擴充方法(例如 Control.GetValidators())來做上面這件事。



评论

请稍候...
很抱歉,您输入的评论太长。请缩短您的评论。
您没有输入任何内容,请重试。
很抱歉,我们当前无法添加您的评论。请稍后重试。
若要添加评论,需要您的家长授予您相应权限。请求权限
您的家长禁用了评论功能。
很抱歉,我们当前无法删除您的评论。请稍后重试。
您已超过了一天之内允许提供的评论数上限。请在 24 小时后重试。
因为我们的系统表明您可能在向其他用户提供垃圾评论,您的帐户已禁用了评论功能。如果您认为我们错误地禁用了您的帐户,请联系 Windows Live 支持部门
完成下面的安全检查,您提供评论的过程才能完成。
您在安全检查中键入的字符必须与图片或音频中的字符一致。

若要添加评论,请使用您的 Windows Live ID 登录(如果您使用过 Hotmail、Messenger 或 Xbox LIVE,您就拥有 Windows Live ID)。登录


还没有 Windows Live ID 吗?请注册

引用通告

此日志的引用通告 URL 是:
http://eggttball.spaces.live.com/blog/cns!330312E5514C141B!1660.trak
引用此项的网络日志