The Will Will Web

記載著 Will 在網路世界的學習心得與技術分享

DropDownList 在作 DataBind 的時候發生 Exception

我們在開發 ASP.NET 時,因為大部分資料都是從資料庫中讀取出來的,所以很常見的情況是在用 DropDownList 時,其選項(ListIem)是透過資料庫 DataBinding 過來的,而在建置管理介面的「編輯」頁面時,DropDownList 都要預設選中資料庫中的那個項目!

不過很不幸的,接到手的資料庫不見得是當初我們規劃的,常會接到那中資料庫表格間完全沒有設定關連的設計,導致資料庫中的內容發生「不一致」的情況。

舉個例子來說好了,假設有個「會員資料」表格中有個欄位是「會員等級」,而「會員等級」是定義在另一個表格,這兩個表格就要設定關連,但是「前人」卻沒有設定 Foreign Key,導致「某人」可能手動更新了「會員等級」資料表中的主鍵內容,造成在「會員表格」中關連不到「會員等級」的情況!

像是這樣的情況,使用 DropDownList 做 DataBinding 的時候就會發生 Exception,而且還沒辦法在 Code Behind 的地方做 try/catch 的動作!想必造成不少人困擾!!

而我們的解決方法是自己寫一個 WebControl 類別,且直接繼承 System.Web.UI.WebControls.DropDownList 類別,並覆寫(overwrite) PerformDataBinding 方法!

[code:c#]
    public class DropDownList2 : DropDownList
    {
        protected override void PerformDataBinding(System.Collections.IEnumerable dataSource)
        {
            // 過濾掉 DropDownList 在 DataBinding 時可能會發生的 Exception 問題!
            // 如果 Exception 發生,預設會選取第一個下拉選項!
            try
            {
                base.PerformDataBinding(dataSource);
            }
            catch (ArgumentOutOfRangeException ex)
            {
                ArgumentOutOfRangeException o = ex;
            }
        }
    }
[/code]

以上這段程式碼就會讓 DropDownList 在做 DataBinding 時不會發生 Exception 了,如果發生 base.PerformDataBinding() 執行失敗的時候,預設是會選中下拉選單中的「第一個選項」。

最後,要在 web.config 中註冊這個 WebControl 讓頁面可以使用,這邊就有兩種用法了:

1. 將元件註冊到 web.config 並自訂 tagPrefix

如果你是將這個 WebControl 放在 App_Code 目錄下的話,要用以下的方式宣告:

[code:html]
<configuration>
    <system.web>
      <pages>
        <controls>
          <add tagPrefix="Doggy" namespace="Doggy.UI.WebControls" assembly="App_Code, Doggy.UI.WebControls.DropDownList2"/>
        </controls>
      </pages>
    </system.web>
</configuration>
[/code]

至於使用方法會像如下範例:

[code:html]
<%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>DropDownList2 測試案例</title>
</head>
<body>
    <form id="form1" runat="server">
        <h1>測試案例 1 : DropDownList2</h1>   
        <Doggy:DropDownList2 runat="server" ID="DropDownList1">
            <asp:ListItem Text="List 1" Value="1"></asp:ListItem>
            <asp:ListItem Text="List 2" Value="2"></asp:ListItem>
            <asp:ListItem Text="List 3" Value="3"></asp:ListItem>
            <asp:ListItem Text="List 4" Value="4"></asp:ListItem>
            <asp:ListItem Text="List 5" Value="5"></asp:ListItem>
        </Doggy:DropDownList2>
    </form>
</body>
</html>
[/code]

2. 第二種比較狠一點,直接將內建的 DropDownList 都換成我們的 DropDownList2

透過 tagMapping 的方式,可以將整個網站內用到 <asp:DropDownList> 的 WebControl 都改用我們自訂的 DropDownList2 類別!

[code:html]
<configuration>
  <system.web>
    <pages>
      <tagMapping>
        <add tagType="System.Web.UI.WebControls.DropDownList"
             mappedTagType="Doggy.UI.WebControls.DropDownList2" />
      </tagMapping>
    </pages>
  </system.web>
</configuration>
[/code]

至於使用的方法,就跟用 <asp:DropDownList> 完全一模一樣啦!

[code:html]
<%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>DropDownList2 測試案例</title>
</head>
<body>
    <form id="form1" runat="server">
        <h1>測試案例 1 : DropDownList2</h1>   
        <asp:DropDownList runat="server" ID="DropDownList1">
            <asp:ListItem Text="List 1" Value="1"></asp:ListItem>
            <asp:ListItem Text="List 2" Value="2"></asp:ListItem>
            <asp:ListItem Text="List 3" Value="3"></asp:ListItem>
            <asp:ListItem Text="List 4" Value="4"></asp:ListItem>
            <asp:ListItem Text="List 5" Value="5"></asp:ListItem>
        </asp:DropDownList>
    </form>
</body>
</html>
[/code]