﻿# 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 方法！

```csharp
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;
        }
    }
}
```

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

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

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

> 如果你是將這個 WebControl 放在 App\_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>
> ```
> 
> 至於使用方法會像如下範例：
> 
> ```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>
> ```

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

> 透過 tagMapping 的方式，可以將整個網站內用到 <asp:DropDownList> 的 WebControl 都改用我們自訂的 DropDownList2 類別！
> 
> ```html
> <configuration>
>   <system.web>
>     <pages>
>       <tagMapping>
>         <add tagType="System.Web.UI.WebControls.DropDownList"
>              mappedTagType="Doggy.UI.WebControls.DropDownList2" />
>       </tagMapping>
>     </pages>
>   </system.web>
> </configuration>
> ```
> 
> 至於使用的方法，就跟用 <asp:DropDownList> 完全一模一樣啦！
> 
> ```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>
> ```
