使用泛型類別解決 ComboBox 的自訂資料繫結問題

摘要:使用泛型類別解決 ComboBox 的自訂資料繫結問題

為了要在 ComboBox 的第一個項目加入一個空白項目,例如:「請選擇」,又不想在資料庫裡面加入這個空白項目,因此只好放棄原本的資料繫結功能(即 DataSource, DisplayMember, 和 ValueMember),而得改用其他方法(姑且稱之為自訂資料繫結)。試了幾種寫法之後,最後決定用泛型類別。

這篇文章主要就是紀錄這個解決問題的過程和方法。只是標題下得不是很好,不過我也想不出更簡潔又明暸的標題了。

第一次的解決方法,是另外建一個 Hashtable 物件,把 ComboBox 裡面要填入的資料先從 DataSet 中逐一取出並填入 Hashtable 物件。當然,第一項會填入一個空白項目。以下是範例:

      Hashtable htbl = new Hashtable();
      ....

      ComboBox1.Items.Clear();
      ComboBox1.Items.Add("請選擇");
      
      // 把查閱資料填入 ComboBox 時,順便複製一份 key-value pair 到 Hashtable 中.
      foreach (DataRow row in DataSet1.Tables[0].Rows)
      {
        ComboBox1.Items.Add(row["Name"].ToString());
        htbl.Add(row["ID"].ToString(), row["Name"].ToString());
      }

等到需要取得使用者在 ComboBox 中選取的項目所對應的 key 值時,就取出ComboBox 目前選取的文字字串,並以該字串到 Hashtable 中搜尋,取出對應的 key 值。

這個做法的缺點是,ComboBox 裡面有一份顯示的文字項目,Hashtable 裡面也保留了一份,顯得有點浪費。如果 ComboBox 裡面儲存的項目就同時包含了 Key 跟 Value,這樣就比較有效率,也不需要另外維護一個 Hashtable 物件。

因此,接下來的目標就是尋找一種可以儲存 key 跟 value 的物件,屆時只要把這個物件加入到 ComboBox 的 Items 集合就行了。本想自己寫一個,類別名稱就叫做 KeyValuePair,但轉念一想:會不會 .NET Framework 已經有提供了?於是到 MSDN Library 搜尋了一下,結果竟然真的有這個型別。原來 .NET Framework 2.0 增加了一個新的泛型結構: KeyValuePair,似乎就是我要的。

既然有現成的,就直接拿來用了,原本的程式碼便改成這樣:

      KeyValuePair kvp;

      ComboBox1.Items.Clear();

      kvp = new KeyValuePair<string, string>("", "請選擇");
      ComboBox1.Items.Add(kvp);

      foreach (DataRow row in DataSet1.Tables[0].Rows)
      {
        kvp = new KeyValuePair<string, string>(row["ID"].ToString(), row["Name"].ToString());
        ComboBox1.Items.Add(kvp);
      }

看起來不錯,可是實際執行時,ComboBox 裡面顯示的每一個項目,卻是把 key 和 value 同時顯示出來。這是由於 KeyValuePair 的 ToString() 方法會把 key 和 value 都轉成字串,並且把它們串接成一個字串的緣故。

由於 KeyValuePair 是結構,無法繼承,也就無法改寫它的 ToString() 方法。看來只好自己寫一個了,還好也不是很複雜,程式碼如下:

  public struct KeyValuePairEx<TKey, TValue>
  {
    private TKey mKey;
    private TValue mValue;

    public KeyValuePairEx(TKey key, TValue value)
    {
      mKey = key;
      mValue = value;
    }

    public TKey Key
    {
      get
      {
        return mKey;
      }
      set
      {
        mKey = value;
      }
    }

    public TValue Value
    {
      get
      {
        return mValue;
      }
      set
      {
        mValue = value;
      }
    }

    public override string ToString()
    {
      return mValue.ToString();
    }
  }

這個自訂的 KeyValuePairEx 結構唯一特別的地方,就是 ToString() 方法只會傳回 value 字串,以便讓 ComboBox 顯示時,只顯示 value,不顯示 key。

把自訂的 KeyValuePairEx 寫好之後,只要把之前的程式碼的 KeyValuePair 替換成 KeyValuePairEx 就行了,因為兩者的用法完全一樣,如下:

      KeyValuePairEx kvp;

      ComboBox1.Items.Clear();

      kvp = new KeyValuePairEx<string, string>("", "請選擇");
      ComboBox1.Items.Add(kvp);

      foreach (DataRow row in DataSet1.Tables[0].Rows)
      {
        kvp = new KeyValuePairEx<string, string>(row["ID"].ToString(), row["Name"].ToString());
        ComboBox1.Items.Add(kvp);
      }

結語:

解題的方式有很多,也不是一定要用泛型啦,用 object 也可以儲存任何物件,只是需要手動轉型,而且缺乏編譯時期的型別安全檢查。