
はじめに
System.Windows.Forms.DataGridコントロール(以下DataGrid)にComboBoxコントロールを表示するもっとも単純な方法は、マイクロソフトサポート技術情報の「[323167 - HOWTO] Windows フォームの DataGrid コントロールに ComboBox コントロールを追加する方法」(現在は閲覧できません)にあるように、DataGridにComboBoxをのせ、DataGridのイベントを利用してComboBoxを操作するという方法でしょう。しかし、DataGridColumnStyleクラス(System.Windows.Forms名前空間)から派生した独自の列クラスを作成することにより、ComboBoxを管理するというやり方の方がよりスマートです。ここでは後者の方法について解説し、実際にComboBoxを管理する列クラス(DataGridComboBoxColumnクラス)のコード例を示します。
対象読者
System.Windows.Forms.DataGridコントロールに任意のコントロールを追加する方法を知りたいという方を対象としています。とりあえずComboBoxを追加したいという方は、「DataGridComboBoxColumnの使用法」からお読みください。ここでは.NETプログラミングの基本的な事柄については説明しませんので、不明な点はMSDNライブラリ等で調べてください。
必要な環境
サンプルはVisual Studio .NET 2003で作成され、.NET Framework 1.1で動作確認をしていますが、.NET Framework 1.0でも問題なく動作するでしょう。
独自の列クラスの作成
DataGridにデータをどのように表示し、ユーザーがどのように編集できるようにするかという管理を行うのが、DataGridColumnStyleオブジェクトです。DataGridColumnStyleクラスは抽象クラスですので、実際にはその派生クラスのインスタンスということになります。.NET FrameworkではDataGridColumnStyleの派生クラスとして、TextBoxを管理するDataGridTextBoxColumnクラスと、CheckBoxを表示するDataGridBoolColumnクラスの2つが用意されています(.NET Farmework 1.1現在。将来はもっと増え、ComboBoxを管理するクラスも追加されるでしょう)。
.NET Frameworkで用意されている列クラスはこの2つだけですので、ComboBoxやその他のコントロールを使った列スタイルをDataGridで使用したい時は、独自で列クラスを作成しなければなりません。この独自の列クラスももちろんDataGridColumnStyleを継承する必要があります。
DataGridColumnStyleは抽象クラスですので、派生クラスでは最低でも次の抽象メソッドをオーバーライドする必要があります。
| メソッド | 説明 |
Abort | 編集操作が中断される時([Escape]キーが押された時や、DataGrid.EndEditがshouldAbortパラメータTrueで呼び出された時など)に実行されます。通常はここで、編集中の値を元に戻し、編集に用いたコントロールを非表示にします。 |
Commit | 編集が完了する時(現在のセルが別のセルに変わる時など)に実行されます。通常はここで、SetColumnValueAtRowメソッドによりセルの値を更新し、編集に用いたコントロールを非表示にします。 |
Edit | 指定されたセルが編集状態になる時に実行されます。通常はここで、編集に用いるコントロールを表示します。初期値は、GetColumnValueAtRowメソッドにより取得します。 |
GetMinimumHeight | 行の最小の高さを返すようにします。 |
GetPreferredHeight | 指定された値を表示するのに適切な行の高さを返すようにします。ユーザーが行の境界線をダブルクリックして列の高さが自動的に変更される時などに使用されます。 |
GetPreferredSize | 指定された値を表示するのに適切なサイズを返すようにします。ユーザーが列の境界線をダブルクリックして列の幅が自動的に変更される時などに使用されます。 |
Paint | 指定されたセルを描画します(境界線を除く)。例えば文字列を表示する時は、GetColumnValueAtRowメソッドにより値を取得し、表示用の文字列に変換し、Graphics.DrawStringメソッドにより実際に描画します。もちろん図形や画像を描画してもOKです。 |
このようにDataGridColumnStyleクラスを直接継承して列クラスを作成するのが基本ですが、既存の列クラスを継承した方が便利な場合もあります。特にDataGridTextBoxColumnクラスでは書式等を指定してデータを表示させる処理がすでに実装されていますので、この部分のコードを書く手間が省けます。大雑把に言えば、DataGridComboBoxColumnはDataGridTextBoxColumnのTextBoxをComboBoxに置き換えたものですので、DataGridTextBoxColumnを継承する利点は多いと考えられます。よってここではDataGridTextBoxColumnクラスを継承することにより、DataGridComboBoxColumnクラスを作成することにします。
DataGridTextBoxColumnクラスを継承してDataGridComboBoxColumnクラスを作成すると、セルでのデータの表示はDataGridTextBoxColumnと全く同じで構いませんので、Paintメソッドを実装する必要がなくなります。ただし、EditやCommitメソッド等は、DataGridColumnStyleから派生する場合と同様にオーバーライドし、適当な処理を行わなければなりません。また、ComboBoxの親コントロールをDataGridとするために、SetDataGridInColumnメソッドをオーバーライドすることも必要です。
さらに、DataGridComboBoxColumnでは表示に使う値と実際の値が異なりますので(詳しくは下の使用法をご覧ください)、SetColumnValueAtRow及びGetColumnValueAtRowメソッドをオーバーライドし、セルに適切な値が表示されるようにします(これとは別の方法も考えられます)。
この他にも細かい処理が必要ですが、詳しくはサンプルのソースコードをご覧ください。
DataGridComboBoxColumnの使用法
ここからは、DataGridComboBoxColumnの使用法を説明します。サンプルのソースコードに実際の使用例がありますので、そちらもあわせてご覧ください。
- このページからリンクされているデモサンプルをダウンロードし、書庫ファイル内の「DataGridComboBoxColumn.dll」を適当な場所に展開します。
DataGridComboBoxColumnクラスを使用したいプロジェクトを開き、「DataGridComboBoxColumn.dll」を参照に追加します。詳しくはMSDNの「参照の追加と削除」をご覧ください。- DataGrid内で
ComboBoxを表示したい列の列スタイルにDataGridComboBoxColumnオブジェクトを使用するために、次のようなコードを書きます(フォームのLoadイベントハンドラ等にお書きください)。ここでは、フォームに「DataGrid1」という名前のDataGridコントロールが配置されているものとします。 - 以上で終了です。成功すれば、上の図のようになります。
//DataGridに表示するDataTableの作成 DataTable dt = new DataTable("DataTable1"); //列の追加 dt.Columns.Add("Column1", typeof(int)); //行の追加 dt.Rows.Add(new object[] {5}); dt.Rows.Add(new object[] {9}); dt.Rows.Add(new object[] {3}); //DataGridで表示するデータソースに設定 DataGrid1.DataSource = dt; //DataGridTableStyleの作成 DataGridTableStyle ts = new DataGridTableStyle(); ts.MappingName = "DataTable1"; //DataGridComboBoxColumnで使用するDataTableの作成 //"DisplayMember"列はComboBoxに表示される値 //"ValueMember"列は実際の値 DataTable comboSorce = new DataTable("ComboBox"); comboSorce.Columns.Add("DisplayMember", typeof(string)); comboSorce.Columns.Add("ValueMember", typeof(int)); comboSorce.Rows.Add(new object[] {"一", 1}); comboSorce.Rows.Add(new object[] {"二", 2}); comboSorce.Rows.Add(new object[] {"三", 3}); comboSorce.Rows.Add(new object[] {"四", 4}); comboSorce.Rows.Add(new object[] {"五", 5}); comboSorce.Rows.Add(new object[] {"六", 6}); comboSorce.Rows.Add(new object[] {"七", 7}); comboSorce.Rows.Add(new object[] {"八", 8}); comboSorce.Rows.Add(new object[] {"九", 9}); //DataGridComboBoxColumnの作成 Dobon.Forms.DataGridComboBoxColumn cbc = new Dobon.Forms.DataGridComboBoxColumn( comboSorce.DefaultView,"DisplayMember","ValueMember"); //表示される値と実際の値が同じでよければ、次のようにもできる //Dobon.Samples.Forms.DataGridComboBoxColumn cbc = // new Dobon.Samples.Forms.DataGridComboBoxColumn( // comboSorce.DefaultView, "ValueMember"); cbc.MappingName = "Column1"; cbc.HeaderText = "数字"; //列スタイルの追加 ts.GridColumnStyles.Add(cbc); //テーブルスタイルの追加 DataGrid1.TableStyles.Add(ts);
'DataGridに表示するDataTableの作成 Dim dt As New DataTable("DataTable1") '列の追加 dt.Columns.Add("Column1", GetType(Integer)) '行の追加 dt.Rows.Add(New Object() {5}) dt.Rows.Add(New Object() {9}) dt.Rows.Add(New Object() {3}) 'DataGridで表示するデータソースに設定 DataGrid1.DataSource = dt 'DataGridTableStyleの作成 Dim ts As New DataGridTableStyle ts.MappingName = "DataTable1" 'DataGridComboBoxColumnで使用するDataTableの作成 '"DisplayMember"列はComboBoxに表示される値 '"ValueMember"列は実際の値 Dim comboSorce As New DataTable("ComboBox") comboSorce.Columns.Add("DisplayMember", GetType(String)) comboSorce.Columns.Add("ValueMember", GetType(Integer)) comboSorce.Rows.Add(New Object() {"一", 1}) comboSorce.Rows.Add(New Object() {"二", 2}) comboSorce.Rows.Add(New Object() {"三", 3}) comboSorce.Rows.Add(New Object() {"四", 4}) comboSorce.Rows.Add(New Object() {"五", 5}) comboSorce.Rows.Add(New Object() {"六", 6}) comboSorce.Rows.Add(New Object() {"七", 7}) comboSorce.Rows.Add(New Object() {"八", 8}) comboSorce.Rows.Add(New Object() {"九", 9}) 'DataGridComboBoxColumnの作成 Dim cbc As New Dobon.Forms.DataGridComboBoxColumn( _ comboSorce.DefaultView, "DisplayMember", "ValueMember") '表示される値と実際の値が同じでよければ、次のようにもできる 'Dim cbc As New Dobon.Forms.DataGridComboBoxColumn( _ ' comboSorce.DefaultView, "ValueMember") cbc.MappingName = "Column1" cbc.HeaderText = "数字" '列スタイルの追加 ts.GridColumnStyles.Add(cbc) 'テーブルスタイルの追加 DataGrid1.TableStyles.Add(ts)
DataGridComboBoxColumnクラスのコンストラクタには3つのパラメータがあり、ComboBoxのDataSourceと、DisplayMember、ValueMemberをここで指定します。DisplayMemberにはComboBoxとDataGridで表示される値を指定し、ValueMemberには実際の値を指定します。上のコードでは、1から9までの数字を漢数字で表示されるようにしています。
DataGridComboBoxColumnの仕様
最後に上記以外のDataGridComboBoxColumnクラスの仕様を示します。
- コンボボックスの
DropDownStyleプロパティはComboBoxStyle.DropDownListです。 ReadOnlyの時は、コンボボックスが表示されません。ComboBoxのDataSourceをDataGridのDataSourceと同じにすると、不都合があるかもしれません。ComboBoxをキーボードで操作することはできません。ComboBoxのValueMemberに登録されていないデータがDataGridで使用されている場合、「DBNull」として表示されます。
おまけ - DataGridButtonColumnクラス
おまけとして、DataGridButtonColumnクラスを付けました(あくまでただのおまけと思ってください)。このクラスは、DataGridにButtonコントロールを表示する列クラスです。DataGridにButtonを表示する方法もComboBoxの場合と同じだと思われるかもしれませんが、Buttonの場合はEditメソッドでコントロールを表示していると、1回目のクリックでButtonを表示し、2回目のクリックでようやく本当にButtonをクリックできるという、好ましくない挙動となってしまいます。よってDataGridButtonColumnクラスでは、実際にはButtonコントロールを使用せずに、ただButtonをセルに描画しているだけです。.NET FrameworkのDataGridBoolColumnはCheckBoxを表示しますが、やはりCheckBoxコントロールを使用しているわけではありません。さらに詳しくは、メールマガジン「.NETプログラミング研究」第37号「DataGridにLinkLabelを表示するには?」をご覧ください。
まとめ
ここではSystem.Windows.Forms.DataGridコントロールにComboBoxコントロールを表示するための列クラスの作成法を紹介しました。ポイントは、以下の通りです。
System.Windows.Forms.DataGridコントロールに任意のコントロールを追加するには、DataGridColumnStyleクラスから派生した列クラスを作成し、使用すると良い。DataGridColumnStyleクラスは抽象クラスなので、すべての抽象メンバをオーバーライドしなければならない。詳細は、「表1.DataGridColumnStyleクラスの抽象メソッド」にある。DataGridColumnStyleを直接継承するよりも、DataGridTextBoxColumnなどの既存の列クラスを継承したほうが手間が省ける場合もある。
参考資料
- DOBON.NET 『.NET Tips - DataGridでComboBoxを使う』
- Akadia AG 『How to put a combobox in a column of a datagrid ?』
- GotDotNet 『GotDotNet User Sample: DataGridComboBoxColumn』
- C# Corner 『Generating Combo Box in DataGrid Columns』 Sudhakar Jalli 著
- DataGridExtensions.DataGridComboBoxColumnStyle by Mark Boulter(Microsoft)
- MSDN Magazine 『DataGrid: Tailor Your DataGrid Apps Using Table Style and Custom Column Style Objects』 Kristy Saunders 著、2003年8月
- MSDNライブラリ 『Styling with the DataGridColumnStyle, Part 2』 Chris Sano 著、2005年1月
