首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

C# 解決 ListBox 設定 DataSource 後無法修改 Items 集合

     編輯:關於C#

問題原因

ListBox.DataSource 綁定集合時,Items 集合變為**唯讀代理**,直接呼叫 Items.Add/Remove/Clear 會拋 InvalidOperationException:「設定 DataSource 屬性後無法修改項集合」。

解決策略:操作資料源而非 Items

**核心原則**:修改綁定的資料源集合 (List<T>, BindingList<T>, DataTable),再重新指派 DataSource 或呼叫 ResetBindings()。

通用泛型輔助類別


using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Windows.Forms;

public static class ListBoxDataSourceHelper
{
    // 取得目前綁定的 List (自動建立/複製)
    private static List GetSourceList(ListBox lb)
    {
        if (lb.DataSource is List list) return list;
        var newList = new List();
        if (lb.DataSource != null)
        {
            // 嘗試從 IEnumerable 複製
            foreach (var item in (IEnumerable)lb.DataSource)
                if (item is T t) newList.Add(t);
        }
        lb.DataSource = null; // 先解除綁定
        lb.DataSource = newList;
        return newList;
    }

    // 取得 DataTable (針對 T = DataRow)
    private static DataTable GetSourceTable(ListBox lb)
    {
        if (lb.DataSource is DataTable dt) return dt;
        var newDt = new DataTable();
        if (lb.DataSource is DataView dv) newDt = dv.Table.Copy();
        else if (lb.DataSource is DataTable dt2) newDt = dt2.Copy();
        lb.DataSource = null;
        lb.DataSource = newDt;
        return newDt;
    }

    public static void AddItem(ListBox lb, T item)
    {
        if (typeof(T) == typeof(DataRow))
            GetSourceTable(lb).Rows.Add(((DataRow)(object)item).ItemArray);
        else
            GetSourceList(lb).Add(item);
        lb.DisplayMember = lb.DisplayMember; // 觸發重新整理
    }

    public static void AddRange(ListBox lb, IEnumerable items)
    {
        if (typeof(T) == typeof(DataRow))
        {
            var dt = GetSourceTable(lb);
            foreach (DataRow row in (IEnumerable)items)
                dt.Rows.Add(row.ItemArray);
        }
        else
        {
            GetSourceList(lb).AddRange(items);
        }
        lb.DisplayMember = lb.DisplayMember;
    }

    public static void RemoveItem(ListBox lb, T item)
    {
        if (typeof(T) == typeof(DataRow))
            GetSourceTable(lb).Rows.Remove((DataRow)(object)item);
        else
            GetSourceList(lb).Remove(item);
        lb.DisplayMember = lb.DisplayMember;
    }

    public static void RemoveWhere(ListBox lb, Predicate match)
    {
        if (typeof(T) == typeof(DataRow))
        {
            var dt = GetSourceTable(lb);
            for (int i = dt.Rows.Count - 1; i >= 0; i--)
                if (match((T)(object)dt.Rows[i])) dt.Rows.RemoveAt(i);
        }
        else
        {
            GetSourceList(lb).RemoveAll(match);
        }
        lb.DisplayMember = lb.DisplayMember;
    }

    public static void ClearAll(ListBox lb)
    {
        if (typeof(T) == typeof(DataRow))
            GetSourceTable(lb).Clear();
        else
            GetSourceList(lb).Clear();
        lb.DisplayMember = lb.DisplayMember;
    }

    public static void MoveItems(ListBox from, ListBox to, IEnumerable selectedItems)
    {
        var items = selectedItems.Cast().ToList();
        AddRange(to, items);
        foreach (var item in items) RemoveItem(from, item);
    }

    public static void MoveAll(ListBox from, ListBox to)
    {
        if (typeof(T) == typeof(DataRow))
        {
            var dt = (DataTable)from.DataSource;
            AddRange(to, dt.Rows.Cast().Select(r => (T)(object)r));
            from.DataSource = dt.Clone(); // 保留結構清空資料
        }
        else
        {
            AddRange(to, (List)from.DataSource);
            from.DataSource = new List();
        }
    }
}

使用範例


// 綁定泛型列表
var list = new List { "A", "B", "C" };
lbSource.DataSource = list;
lbSource.DisplayMember = ""; // 顯示字串本身

// 移動選取項目到另一個 ListBox
ListBoxDataSourceHelper.MoveItems(lbSource, lbTarget, lbSource.SelectedItems);

// 新增項目
ListBoxDataSourceHelper.AddItem(lbSource, "New Item");

// 移除符合條件項目
ListBoxDataSourceHelper.RemoveWhere(lbSource, s => s.StartsWith("A"));

進階:使用 BindingList 獲得自動通知


var bindingList = new BindingList { "A", "B", "C" };
lbSource.DataSource = bindingList;
// 直接操作 bindingList 即可自動更新 UI,無需重設 DataSource
bindingList.Add("D"); // UI 即時新增
bindingList.RemoveAt(0); // UI 即時移除
  • BindingList<T> 實作 IBindingList,支援新增/移除/排序通知。
  • **推薦新專案直接使用 BindingList<T> 作為 DataSource**,完全避免手動重設 DataSource 的麻煩。

DataTable 綁定特殊處理


var dt = new DataTable();
dt.Columns.Add("Name");
dt.Columns.Add("Value");
dt.Rows.Add("Item1", 100);
dt.Rows.Add("Item2", 200);

lbSource.DataSource = dt;
lbSource.DisplayMember = "Name"; // 顯示 Name 欄位
lbSource.ValueMember = "Value";  // 實際值

// 移除選取行
if (lbSource.SelectedItem is DataRowView drv)
    drv.Row.Delete(); // 標記刪除
dt.AcceptChanges();   // 確認刪除

總結

  1. **不要嘗試修改 ListBox.Items**,會拋例外。
  2. **操作 DataSource 集合** (List<T>, BindingList<T>, DataTable)。
  3. 首選 BindingList<T> 獲得自動 UI 同步,最省碼、最穩健。
  4. 若必須用 List<T>,操作後重設 DataSource = null; DataSource = list; 或呼叫 lb.DisplayMember = lb.DisplayMember; 觸發重繪。

遵循「資料驅動 UI」原則,ListBox 資料操作即變得簡單可靠。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved