11723
お世話になります。

C#のListBoxで、ValueMemberとDisplayMemnerを設定した後、  
自由に項目を削除したいと考えています。

下記の様なソースで、初めにDataTableで管理できるようにし、addItemメソッドで  
項目を追加するところまで作成しました。

```

    public void init()
    {
        this.Font = new System.Drawing.Font("ＭＳゴシック", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(128)));
        this.ScrollAlwaysVisible = true;
    
        this.Items.Clear();
    
        //DataTableオブジェクトを用意
        dtItem = new DataTable();
    
        //DataTableに列を追加
        dtItem.Columns.Add("VALUE", typeof(string));
        dtItem.Columns.Add("NAME", typeof(string));
    
        //リストボックスのDataSourceにDataTableを割り当てる
        this.DataSource = dtItem;
    
        //表示される値はDataTableのNAME列
        this.DisplayMember = "NAME";
    
        //対応する値はDataTableのValue列
        this.ValueMember = "VALUE";
    
    }
    
    public void addItem(string id,string name)
    {
        //新しい行を作成
        DataRow row = dtItem.NewRow();
    
        //各列に値をセット
        row["VALUE"] = id;
        row["NAME"] = name;
        dtItem.Rows.Add(row);
    }
    
```

ここから、removeItem(string id)などのようなメソッドで、  
項目が削除できると良いのですが、DataTable.Rows.Removeメソッドなどでは  
DataRowを渡さなければならなかったり、どのように削除を行えばよいのか、  
途方に暮れています。

解決方法をお教えいただけないでしょうか。

よろしくお願いいたします。
CC BY-SA 3.0
2015-06-25T07:39:33.097
11713
2015-06-25T11:00:49.517
2015-06-25T08:38:39.630
8870
9374
question
c# winforms
C# ValueMemberやDisplayMemberを設定したListBoxから項目を削除する方法
ソースがDataTableと予め判っているなら、DataViewを作ってFindRowsするのが楽です。  
そうでなくソースが不定な状態であるなら以下のように、ListBox.DataManagerからCurrentcyManagerを取って、そこからDataViewを取り出すことができます。

DataViewが取れれば後はValueMemberをキーにして検索するとDataRowViewが得られるので、そのDataRowView.Rowが対象のDataRowになります。

```

    public void RemoveItemByID(string id) //このコードはListBoxを継承したクラス内のいうメソッド
    {
        var manager = this.DataManager as CurrencyManager;
        if (manager == null)
        {
            return;
        }
    
        if (manager.Count == 0)
        {
            return;
        }
    
        var dv = manager.List as System.Data.DataView;
        if (dv != null && false)
        {
            var dt = dv.Table;
            var column = dt.Columns[this.ValueMember];
            if (dt.PrimaryKey != null && dt.PrimaryKey.Length == 1 && dt.PrimaryKey[0] == column)
            {
                //プライマリーキーならそのままをキーにして削除
                int index = dv.Find(id);
                dv.Delete(index);
            }
            else
            {
                //見つかったのを入れておくリスト
                List<DataRow> foundRows = new List<DataRow>();//プライマリーキーでない場合は複数見つかる可能性がある
    
                if (false)
                {
                    //プライマリーキーでない場合は一時的にDataViewを作って
                    //そのDataViewで検索を行う
                    var dvTemp = new System.Data.DataView(dv.Table);
                    dvTemp.Sort = this.ValueMember;//検索する列名を指定
    
    
                    foreach (DataRowView drv in dvTemp.FindRows(id))
                    {
                        foundRows.Add(drv.Row);
                    }
                    //見つかったのを削除
                    foreach (DataRow row in foundRows)
                    {
                        dv.Table.Rows.Remove(row);
                    }
                }
                else
                {
                    //DataViewを作るまでもないなら自分で探す
                    foreach (DataRowView drv in dv)
                    {
                        object value = drv[this.ValueMember];
                        if ((value == null && id == null)
                        || (value != null && id == value.ToString()))
                        {
                            foundRows.Add(drv.Row);
                        }
                    }
                }
    
                foreach (DataRow row in foundRows)
                {
                    dv.Table.Rows.Remove(row);
                }
            }
    
        }
        else
        {
            //DataViewでないならIBindingListか調べる
            var bindingList = manager.List as System.ComponentModel.IBindingList;
            if (bindingList != null)
            {
                var props = System.ComponentModel.TypeDescriptor.GetProperties(bindingList[0]);
                foreach (System.ComponentModel.PropertyDescriptor pd in props)
                {
                    if (pd.Name == this.ValueMember)
                    {
                        var index = bindingList.Find(pd, id);
                        if (index >= 0)
                        {
                            bindingList.RemoveAt(index);
                        }
                        break;
                    }
                }
            }
            else
            {   //IBindingListですらないなら全部をリフレクションで
                for (int index = this.Items.Count - 1; index >= 0; --index)
                {
                    object o = this.Items[index];
                    if (o != null)
                    {
                        var pi = o.GetType().GetProperty(this.ValueMember);
                        if (pi != null)
                        {
                            object value = pi.GetValue(o, null);
                            if ((value == null && id == null)
                            || (value != null && id == value.ToString()))
                            {
                                this.Items.Remove(index);
                            }
                        }
                    }
                }
            }
        }
    }
    
```
CC BY-SA 3.0
2015-06-25T10:54:08.737
11721
2015-06-25T10:54:08.737
4926
11713
answer
一番基礎的な方法はループによるものです。削除時にインデックスが変わるため逆順ループにしています。

```

    for (var i = dtItem.Rows.Count - 1; i >= 0; i--)
    {
        var row = dtItem.Rows[i];
        if (id.Equals(row["VALUE"]))
        {
            dtItem.Rows.Remove(row);
            // 一件削除ならbreak;する
        }
    }
    
```

LINQを使う場合は以下の通りです。

```

    var row = dtItem.AsEnumerable().FirstOrDefault(_ => _.Field<string>("VALUE") == id);
    if (row != null)
    {
        dtItem.Rows.Remove(row);
    }
    
```

複数行削除であれば`ToArray()`などで削除対象を一時的なリストに格納します。

```

    var rows = dtItem.AsEnumerable().Where(_ => _.Field<string>("VALUE") == id).ToArray();
    foreach (var row in rows)
    {
        dtItem.Rows.Remove(row);
    }
    
```

レガシーな手法ですが、`DataRow`の抽出には`Select`という手もあります。

```

    var rows = dtItem.Select("VALUE='" + id + "'");
    
    //以下略
    
```

またテーブルに主キーを設定しておけば`Find`で検索できます。

```

    // 一度だけ実行する
    dtItem.PrimaryKey = new[] { dtItem.Columns["VALUE"] };
    
    var row = dtItem.Rows.Find(id);
    
    //以下略
    
```
CC BY-SA 3.0
2015-06-25T11:00:49.517
11723
2015-06-25T11:00:49.517
5750
11713
answer