3流プログラマのメモ書き

元開発職→社内SE→派遣で営業支援→開発戻り浦島太郎状態の三流プログラマのIT技術メモ書き。 このメモが忘れっぽい自分とググってきた技術者の役に立ってくれれば幸いです。

(.Net)DataGridViewへのカスタムクラスのバインド

(.Net)DataGridViewでバインドしたデータソースを変更してもグリッドには反映されない?でカスタムクラスのバインドについて書きました。

あの方法でバインドした DataGridView を下記のようにプログラム側から1行削除しようとしました。

public partial class Form1 : Form

{

//カスタムクラスのList。バインド元になるデータ。

List mlst = new List();

private void button1_Click(object sender, EventArgs e)

{

mlst.Add(new user("hogehoge1", "1"));

mlst.Add(new user("hogehoge2", "2"));

mlst.Add(new user("hogehoge3", "3"));

}

 

private void Form1_Load(object sender, EventArgs e)

{

//バインド。

//dataGridView1.DataSource = mlst;

}

private void button2_Click(object sender, EventArgs e)

{

dataGridView1.Rows.RemoveAt(0);

}

}

 

//カスタムクラス。

public class user

{

public string name { get; set; }

public string id { get; set; }

public user(string sname, string sid)

{

name = sname;

id = sid;

}

}

Button1を押下しレコード追加後、Button2を押下してレコードを削除します。

その時、RemoveAt で下記の例外が発生しました。

System.InvalidOperationException はハンドルされませんでした。

Message="変更通知をサポートし、削除を許可する IBindingList に DataGridView がデータバインドされていない限り、プログラムで行を削除することはできません。"

Source="System.Windows.Forms"

StackTrace:

場所 System.Windows.Forms.DataGridViewRowCollection.RemoveAt(Int32 index)

場所 DataGridViewBindObjectTest.Form1.button2_Click(Object sender, EventArgs e) 場所 D:\mydoc\DataGridViewBindObjectTest\DataGridViewBindObjectTest\DataGridViewBindObjectTest\Form1.cs:行 65

...

で、調査してみました。

するとデータバインディングのおべんきょ。その5。で List をバインド対象として使わないほうがいいと書いてあります。理由として、List クラスが IBindingList インターフェイスを実装してないからみたいですね。

もうちょっと詳しい話が、MSDN:カスタム データ バインド (第 2 部)に下記のように書いてます。

BindingList は IBindingList インターフェイス (System.ComponentModel 名前空間) のジェネリック実装です。また、リスト データ ソースで編集機能をサポートするために、データ バインドインフラストラクチャで最低限必要になるインターフェイスです。IList はバインド可能なリスト データソースの実装に必要な最低限のインターフェイスですが、このインターフェイスでは編集機能は提供されません。これは、ListBox などの編集できないコントロールにバインドされる場合は問題ありません。ただし、DataGridView など、編集機能を完全にサポートしているコントロールにバインドされる場合は、並べ替え、検索、インデックス作成、および変更通知などの機能のサポート以外に、リスト データ ソース内での編集機能が必須になります。IBindingList は IList から派生しているので、このような機能をすべてサポートするように拡張されます。

つまり、IListを継承した Listクラスは編集機能を有さない最低限の機能しかないためダメだったんですね。

かといって、今更BindingListに直すのも面倒なんでいい方法が無いかいろいろ試してみました。

見つけたのが、MSDN:方法 : オブジェクトを Windows フォーム DataGridView コントロールにバインドするです。

BindingSource を使ってますね。

DataGridView → BindingSource → コレクション という感じに使うようです。

ただ、MSDNチュートリアルでは BindingSource の Add メソッドでカスタムクラス単体を追加しています。

本来はこの方法か、iBindingList を実装したクラスでバインドするのが筋なんでしょうが、面倒なので、下記のようにしました。

public partial class Form1 : Form

{

List mlst = new List();

 

private void button1_Click(object sender, EventArgs e)

{

mlst.Add(new user("hogehoge1", "1"));

mlst.Add(new user("hogehoge2", "2"));

mlst.Add(new user("hogehoge3", "3"));

bindingSource1.DataSource = null;

bindingSource1.DataSource = mlst;

}

 

private void Form1_Load(object sender, EventArgs e)

{

DataColumn clm1 = new DataColumn("name");

mTbl.Columns.Add(clm1);

DataColumn clm2 = new DataColumn("id");

mTbl.Columns.Add(clm2);

 

dataGridView1.DataSource = bindingSource1;

}

 

private void button2_Click(object sender, EventArgs e)

{

dataGridView1.Rows.RemoveAt(0);

}

}

 

public class user

{

public string name { get; set; }

public string id { get; set; }

 

public user(string sname, string sid)

{

name = sname;

id = sid;

}

}

BindingSource の DetaSource に List を指定しています。

とりあえずレコードの削除やDataGridView上の変更も List オブジェクトに反映されてるようです。

あんまきれいなやり方では無いですが、とりあえずこれでやってみることとしました。

今まで DataGridView にバインドしてたのは DataTable で楽してたんですが、他のコレクションをバインドするのは結構機能面の制約があって面倒ですね。。。