デベロッパー
デベロッパー
DataGridViewコントロールを操作する101の方法
はじめに
Windows Formsプログラミングで普段よく使われるコントロールの一つにDataGridViewがあります。DataGridViewは、Windows Forms 2.0で新たに登場したコントロールで、Windows Forms 1.0のDataGridコントロールの後継となるものです。非常に強力で用途の広いコントロールであり、各種のデータソースを表形式で表示するのに向いています。柔軟性が高いゆえに、公開されているプロパティ、メソッド、イベントも多数にのぼり、初心者にはかなり取っ付きにくいコントロールと言えます。
本稿は、プログラミングの現場でDataGridViewコントロールをすぐ使いこなせるようにすることを目指しています。一般的なタスクをほぼカバーするよう努めましたが、DataGridViewコントロールの全機能を網羅しているわけではありません。
本稿では、WindowsアプリケーションプロジェクトのデフォルトのフォームForm1にDataGridViewコントロールを配置するものと仮定します(図1)。
それではさまざまなテクニックを見ていきましょう。
配列をバインドする
DataGridViewコントロールに配列をバインドすることができます。次の例を見てください。
Dim arr() As String = _ {"Product 1", "Product 24", "Product 356"} DataGridView1.DataSource = arr
arr()は3個の要素を持つ文字列配列です。DataSourceプロパティを用いてDataGridViewコントロールにバインドされています。図2に、このデータバインドの結果を示します。

この結果は意外なものかもしれません。おそらく、配列の各要素が表示されると予想していた人が多いのではないでしょうか。実際には、各要素の長さが表示されます。DataGridViewコントロールはバインドされたオブジェクトの最初のパブリックプロパティを探すからです。この例では、文字列配列の最初のパブリックプロパティは配列内の各要素の長さなので、上のような結果が得られるわけです。
文字列配列をDataGridViewコントロールに正しくバインドするためには、次に示すように文字列型をラップするクラスを作成し、このクラスが、要素の内容を返すパブリックプロパティNameを公開するようにします。
Public Class CStringItem Private _str As String Public Sub New(ByVal str As String) _str = str End Sub Public Property Name() As String Get Return _str End Get Set(ByVal value As String) _str = value End Set End Property End Class
次の例では、arr()をCStringItem型の配列として宣言し、3個の要素を初期設定した後で、DataGridViewコントロールにバインドします。
’---modified array--- Dim arr() As CStringItem = { _ New CStringItem("Product 1"), _ New CStringItem("Product 2"), _ New CStringItem("Product 3")} DataGridView1.DataSource = arr
図3は、このデータバインドの結果です。パブリックプロパティNameがDataGridViewコントロール内の列の見出しテキストとして使われていることに注意してください。

カスタムオブジェクトをバインドする
次の例では、DataGridViewコントロールにもっと複雑なオブジェクトをバインドする方法を説明します。2つのパブリックプロパティ(Name、ID)を持つCStudentクラスを定義します。
Public Class CStudent Private _name As String Private _ID As String Public Sub New(ByVal id As String, ByVal name As String) _ID = id _name = name End Sub Public Property Name() As String Get Return _name End Get Set(ByVal value As String) _name = value End Set End Property Public Property ID() As String Get Return _ID End Get Set(ByVal value As String) _ID = value End Set End Property End Class
DataGridViewコントロールにCStudentオブジェクトの配列をバインドすると、図4の結果が得られます。
’---binding to custom object--- Dim students() As CStudent = _ {New CStudent("123-456-789", "John"), _ New CStudent("456-123-789", "Mary")} DataGridView1.DataSource = students

型指定されたデータセットを用いてバインドする
DataGridViewコントロールが特によく使われるのは、データベースのテーブルをバインドするケースです。これを説明するために、現在のプロジェクトに型指定されたデータセットを追加します。Visual Studio 2005のソリューションエクスプローラでプロジェクト名を右クリックし、[追加]→[新しい項目の追加]を選択します。データセットテンプレートを選択し(DataSet1.xsdのデフォルトの名前を使用)、[追加]をクリックします。
サーバエクスプローラを起動し([表示]→[サーバエクスプローラ])、「Northwind」サンプルデータベースに移動します(SQL Server/SQL Server Expressでインストールされているものと仮定)。「Customers」テーブルをDataSet1.xsdのデザインサーフェイスにドラッグ&ドロップします。図5は、型指定されたデータセットを作成する様子を示しています。
次のステップには2つの選択肢があります。1つは、次に示すようにDataGridViewコントロールに「Customers」テーブルのテーブルアダプタを直接バインドする方法です。
’---create an instance of the table adapter--- Dim adapter As New CustomersTableAdapter ’---data bind to the DataGridView control--- DataGridView1.DataSource = adapter.GetData
もう1つは、BindingSourceコントロールを使用する方法です。
’---create an instance of the table adapter--- Dim adapter As New CustomersTableAdapter ’---create an instance of the bindingsource control--- Dim bindingSrc As New BindingSource ’---set the datasource for the bindingsource control--- bindingSrc.DataSource = adapter.GetData ’---data bind to the DataGridView control--- DataGridView1.DataSource = bindingSrc
なお、上のコードが正常に実行されるようにするためには、次のように名前空間をインポートしておく必要があります(DVGは、このプロジェクトの名前です)。
Imports DGV.DataSet1TableAdapters
図6は、このデータバインドの結果です。
データセットでバインドする
データセットを手動で作成する場合は、DataSourceプロパティにデータセットを設定し、表示するテーブルをDataMemberプロパティに指定すれば、DataGridViewコントロールにバインドできます。
Dim connStr As String = _ "Data Source=.SQLEXPRESS;Initial Catalog=Northwind;" & _ "Integrated Security=True" Dim sql As String = "SELECT * FROM Customers" Dim conn As SqlConnection = New SqlConnection(connStr) Dim comm As SqlCommand = New SqlCommand(sql, conn) Dim dataadapter As SqlDataAdapter = New SqlDataAdapter(comm) Dim ds As DataSet = New DataSet() ’---open the connection and fill the dataset--- conn.Open() ’---fill the dataset--- dataadapter.Fill(ds, "Customers_table") ’---close the connection--- conn.Close() ’---bind to the DataGridView control--- DataGridView1.DataSource = ds ’---set the table in the dataset to display--- DataGridView1.DataMember = "Customers_table"
クリックされたセルを検出する
ユーザーのクリックしたDataGridViewコントロール内のセルの値を取得する場合は、CellEnterイベントを処理します。
’---when the user clicks on the datagridview control--- Private Sub DataGridView1_CellEnter( _ ByVal sender As Object, _ ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) _ Handles DataGridView1.CellEnter ’---prints the content of the cell--- Console.WriteLine( _ DataGridView1.Rows(e.RowIndex).Cells(e.ColumnIndex).Value) End Sub
RowIndexプロパティとColumnIndexプロパティに、現在選択されているセルの行番号と列番号がそれぞれ設定されます。
DataViewでフィルタリングする
DataGridViewコントロールに表示されるデータにフィルタをかけるにはDataViewオブジェクトを使用します。
’---create an instance of the table adapter--- Dim adapter As New CustomersTableAdapter ’---create an instance of the dataview class--- Dim dv As New DataView(adapter.GetData) With dv .AllowNew = False .AllowDelete = True .Sort = "ContactTitle ASC, Address ASC" .RowFilter = "CustomerID LIKE ’B*’" End With DataGridView1.DataSource = dv
上の例は、DataViewオブジェクトを使うことにより、DataGridViewコントロールにユーザーが新しい行を追加するのを許可したり(AllowNewプロパティ)、行を削除するのを許可したり(AllowDeleteプロパティ)できることを示しています。また、フィールドとソート順序を指定して行をソートすることもできます(Sortプロパティ)。さらに、SQLに似た式を指定してレコードにフィルタをかけることもできます(RowFilterプロパティ)。
上の例では、行をContactTitleフィールドの昇順でソートし、さらにAddressフィールドの昇順でソートしています。Addressフィールドの降順でソートする場合は、次のように設定します。
.Sort = "ContactTitle ASC, Address DESC"
図7に、ContactTitleフィールドとAddressフィールドを共に昇順でソートしたケースと、ContactTitleを昇順、Addressを降順でソートしたケースを示します。
列をソートする
DataViewオブジェクトを使えばフィルタリングやソートを実行できますが、DataGridViewコントロールのSort()メソッドでもソートを制御できます。
’---sort based on the first column---
DataGridView1.Sort(DataGridView1.Columns(0), _
System.ComponentModel.ListSortDirection.Descending)
この例では、行を最初の列の降順でソートしています(図8)。
プログラムによるソートとは別に、ユーザーが実行時にソートグリフ(列見出しの隣に表示される三角形のアイコン)をクリックして行を昇順または降順にソートすることも可能です。行がソートされないようにするには、SortModeプロパティを「NotSortable」に設定します。
’---set the sort mode for a column---
DataGridView1.Columns(1).SortMode = _
DataGridViewColumnSortMode.NotSortable
プログラミングによって列を追加する
これまで紹介した方法は、データセットまたはBindingSourceコントロールを用いて、DataGridViewコントロールにデータベースをバインドするというものでした。しかし、ときにはプログラミングによってDataGridViewコントロール内にさまざまなフィールドを作成し、ユーザーに値を設定させる場合もあります(同様に、プログラミングによって新しい行を追加することもできます)。DataGridViewコントロール内に列を作成するには、DataGridViewコントロールのColumnsプロパティのAdd()メソッドを使用します。次の例では、DataGridViewコントロール内に4つのフィールドを作成しています。
’---adding columns--- DataGridView1.Columns.Add("ID", "Product ID") DataGridView1.Columns.Add("Name", "Product Name") DataGridView1.Columns.Add("Description", "Description") DataGridView1.Columns.Add("Price", "Price")
図9は、このDataGridViewコントロールの表示結果です。

行を追加する
DataGridViewコントロールに行を追加するには、RowsプロパティのAdd()メソッドを使用します。
’---add an empty row---
DataGridView1.Rows.Add()
行を追加してそこに内容を割り当てるには、DataGridViewRowクラスのインスタンスを作成してからCreateCells()メソッドで行テンプレートを作成します。
For i As Integer = 0 To 9 ’---create a row--- Dim item As New DataGridViewRow item.CreateCells(DataGridView1) With item .Cells(0).Value = i .Cells(1).Value = "Product " & i .Cells(2).Value = "Description of Product " & i .Cells(3).Value = "99.99" End With ’---add the row--- DataGridView1.Rows.Add(item) Next
行の各フィールドに割り当てる一連の値を配列に格納し、その配列を使用して新しい行を追加することもできます。
For i As Integer = 0 To 9 DataGridView1.Rows.Add(New String() _ {i, _ "Product " & _ i, _ "Description of Product " & i, _ "99.99"}) Next
図10は、上の2つの例の出力です。
セルにコンボボックスを表示する
セルにテキストを表示するだけでなく、ドロップダウンリストボックスを表示して、あらかじめ決められた値をリストから選択できるようにしたいことがあります。その場合は、目的の列のセルにコンボボックスを追加する必要があります。次の例では、DataGridViewコントロールの5番目の列にComboBoxコントロールを追加しています。
’---add columns to the DataGridView control--- DataGridView1.Columns.Add("ID", "Product ID") DataGridView1.Columns.Add("Name", "Product Name") DataGridView1.Columns.Add("Description", "Description") DataGridView1.Columns.Add("Price", "Price") ’---create a new bindingsource control--- Dim bindingsource As New BindingSource ’---add the items into the control--- bindingsource.Add("Type A") bindingsource.Add("Type B") bindingsource.Add("Type C") ’---create a combobox column--- Dim comboBoxCol As New DataGridViewComboBoxColumn ’---set the header--- comboBoxCol.HeaderText = "Types" ’---data bind it--- comboBoxCol.DataSource = bindingsource ’---add a combobox column to the DataGridView control--- DataGridView1.Columns.Add(comboBoxCol)
この出力結果を図11に示します。
上の例では、項目リストを含んだBindingSourceコントロールを、DataGridViewコントロール内のDataGridViewComboBoxColumnコントロールにバインドする方法を示しましたが、DataGridViewComboBoxColumnコントロールに項目を直接追加する方法もあります。
’---adding columns--- DataGridView1.Columns.Add("ID", "Product ID") DataGridView1.Columns.Add("Name", "Product Name") DataGridView1.Columns.Add("Description", "Description") DataGridView1.Columns.Add("Price", "Price") ’---add a combobox column--- Dim comboBoxCol As New DataGridViewComboBoxColumn ’---set the header text--- comboBoxCol.HeaderText = "Types" ’---add items to it--- comboBoxCol.Items.Add("Type A") comboBoxCol.Items.Add("Type B") comboBoxCol.Items.Add("Type C") DataGridView1.Columns.Add(comboBoxCol)
後者の方がより柔軟であり、この方法で実装していると、実行時にユーザーがドロップダウンリストに新しい項目を追加できます(詳細については次のセクションを参照)。ComboBoxコントロールにデータソースをバインドすると、新しい項目を実行時に追加できなくなります。
DataGridViewComboBoxColumnコントロールに項目を追加する
前のセクションでは、DataGridViewコントロール内のセルにComboBoxコントロールを表示する方法を示しました。ComboBoxコントロールにユーザーが新しい項目を挿入できるようにしたいことがあります。そのためには、少し準備が必要です。
まず、DataGridViewコントロールのEditingControlShowingイベントを処理します。このイベントは、ユーザーがComboBoxコントロールを編集しようとしたときに発生します。
Private Sub DataGridView1_EditingControlShowing( _ ByVal sender As Object, _ ByVal e As System.Windows.Forms. _ DataGridViewEditingControlShowingEventArgs) _ Handles DataGridView1.EditingControlShowing Dim comboBoxColumn As DataGridViewComboBoxColumn = _ DataGridView1.Columns(4) If (DataGridView1.CurrentCellAddress.X = _ comboBoxColumn.DisplayIndex) Then Dim cb As ComboBox = e.Control If (cb IsNot Nothing) Then cb.DropDownStyle = ComboBoxStyle.DropDown End If End If End Sub
ここでのポイントは、編集対象のセルがComboBoxを含むセルかどうかを確認することです。ComboBoxを含んでいる場合は、ComboBoxコントロールのドロップダウンスタイルを「DropDown」に設定して、ユーザーによる追加入力ができるようにします。
次に、DataGridViewコントロールのCellValidatingイベントを処理します。このイベントはユーザーが入力を終了してセルを離れたときに発生します。
Private Sub DataGridView1_CellValidating( _ ByVal sender As Object, _ ByVal e As System.Windows.Forms. _ DataGridViewCellValidatingEventArgs) _ Handles DataGridView1.CellValidating Dim comboBoxColumn As DataGridViewComboBoxColumn = _ DataGridView1.Columns(4) If (e.ColumnIndex = comboBoxColumn.DisplayIndex) Then If (Not comboBoxColumn.Items.Contains( _ e.FormattedValue)) Then comboBoxColumn.Items.Add(e.FormattedValue) End If End If End Sub
ここでは、通常のチェックを行った後に、新しく入力された項目をComboBoxに追加します。図12を見ると、ComboBoxには最初3個の項目があります。ここにユーザーが新しい項目(例えば、「Type D」)を入力でき、それがリストに追加されます。実際に操作した行より前または後ろの行でも、ComboBoxコントロールの項目数が4になることに注意してください。
行/列をロックする
行のReadOnlyプロパティをTrueに設定すると、特定の行が変更されないようにロックできます。
’---first row is readonly--- DataGridView1.Rows(0).ReadOnly = True
同様に、特定の列に属するセルをロックすることもできます。
’---first column is readonly--- DataGridView1.Columns(0).ReadOnly = True
ReadOnlyプロパティをTrueに設定するとセルの値は変更されなくなりますが、行の削除ができなくなるわけではありません。
必要ならDataGridViewコントロール全体をロックすることもできます。
’---entire grid is readonly--- DataGridView1.ReadOnly = True
列を非表示にする
実行時に列のVisibleプロパティをFalseに設定することで、特定の列を非表示にできます。
’---hides the second column--- DataGridView1.Columns(1).Visible = False
図13に、列を非表示にしたDataGridViewの例を示します。奥はすべての列を表示した状態で、手前は2番目の列(CompanyName列)を非表示にした状態です。
ユーザーの編集を検証する
DataGridViewコントロールの一般的な用途の1つはデータ入力です。ユーザーはコントロールの個々のセルにデータを入力します。そのため、正しい種類のデータが入力されたかどうかを検証することが重要です。
入力されたデータの種類が正しいことを検証するには、2つのイベントを処理する必要があります。最初にCellValidatingイベントを処理します。このイベントは、ユーザーがセルの値を変更してセルを離れたときに発生します。
Private Sub DataGridView1_CellValidating( _ ByVal sender As Object, _ ByVal e As System.Windows.Forms. _ DataGridViewCellValidatingEventArgs) _ Handles DataGridView1.CellValidating ’---Price field--- If e.ColumnIndex = 3 Then If Not IsNumeric(e.FormattedValue) Then DataGridView1.Rows(e.RowIndex).ErrorText = _ "Price must be a numeric value." e.Cancel = True End If End If End Sub
このイベント内で必要な検証を行います。例えば上の例では、4番目の列(列インデックスのPriceフィールド)に数値以外が含まれていないことを確認します。数値以外が含まれている場合は、DataGridViewコントロールの一番左の列にエラーメッセージを表示します(ErrorTextプロパティ)。図14に例を示します。
ユーザーはエラーを訂正しないと他のセルに移動できません。訂正データが入力されたときにエラーメッセージが消えるようにするには、CellEndEditイベントを処理します。
Private Sub DataGridView1_CellEndEdit( _ ByVal sender As Object, _ ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) _ Handles DataGridView1.CellEndEdit ’---clear the error message--- DataGridView1.Rows(e.RowIndex).ErrorText = String.Empty End Sub
入力を制限する
前のセクションでは、ユーザーの入力したセルの値を検証する方法を示しました。しかし、状況によっては、これでもまだ十分でありません。最初から不正な文字が入力されないようにした方が良いこともあります。これは、前のセクションの例で考えると、Priceフィールドに数字以外の文字が入力されないようにすることを意味します。普通のTextBoxコントロールでは、この問題はKeyPressイベントを処理することで解決できます。しかし、この方法をDataGridViewコントロールに適用するためには少し準備が必要です。
まず、EditingControlShowingイベントを処理します。このイベントはユーザーがセルの内容を編集しようとしたときに発生します。
Private Sub DataGridView1_EditingControlShowing( _ ByVal sender As Object, _ ByVal e As System.Windows.Forms. _ DataGridViewEditingControlShowingEventArgs) _ Handles DataGridView1.EditingControlShowing ’---restrict inputs on the fourth field--- If Me.DataGridView1.CurrentCell.ColumnIndex = 3 And _ Not e.Control Is Nothing Then Dim tb As TextBox = CType(e.Control, TextBox) ’---add an event handler to the TextBox control--- AddHandler tb.KeyPress, AddressOf TextBox_KeyPress End If End Sub
ここでは、制限したいTextBoxコントロールにKeyPressイベントハンドラを追加します。このKeyPressイベントハンドラは、ユーザーがセルに入力を行ったときに呼び出されます。この定義は次のとおりです。
Private Sub TextBox_KeyPress( _ ByVal sender As System.Object, _ ByVal e As System.Windows.Forms.KeyPressEventArgs) ’---if textbox is empty and user pressed a decimal char--- If CType(sender, TextBox).Text = String.Empty And _ e.KeyChar = Chr(46) Then e.Handled = True Return End If ’---if textbox already has a decimal point--- If CType(sender, TextBox).Text.Contains(Chr(46)) And _ e.KeyChar = Chr(46) Then e.Handled = True Return End If ’---if the key pressed is not a valid decimal number--- If (Not (Char.IsDigit(e.KeyChar) Or _ Char.IsControl(e.KeyChar) Or _ (e.KeyChar = Chr(46)))) Then e.Handled = True End If End Sub
上記のコードは、ユーザーの入力を数字(「.」も含む)にのみ制限します。それ以外の文字が入力されてもセルには現れません。
行を削除する
プログラミングによってDataGridViewコントロール内の行を削除するには、Remove()メソッドを使います。次のコードはDataGridViewコントロール内の選択された行をすべて削除します。
For Each row As DataGridViewRow In DataGridView1.SelectedRows DataGridView1.Rows.Remove(row) Next
ユーザーは最初に行を選択して[Delete]キーを押す方法でも行を削除できます。デフォルトで、この削除は確認なしに自動で実行されます。しかし、削除の前にユーザーに確認を求めることもできます。これにはUserDeletingRowイベントを使います。
Private Sub DataGridView1_UserDeletingRow( _ ByVal sender As Object, _ ByVal e As System.Windows.Forms. _ DataGridViewRowCancelEventArgs) _ Handles DataGridView1.UserDeletingRow If (Not e.Row.IsNewRow) Then Dim response As DialogResult = _ MessageBox.Show( _ "Are you sure you want to delete this row?", _ "Delete row?", _ MessageBoxButtons.YesNo, _ MessageBoxIcon.Question, _ MessageBoxDefaultButton.Button2) If (response = DialogResult.No) Then e.Cancel = True End If End If End Sub
このコードにより、行の削除を実行してよいかを確認するプロンプトが表示されます(図15)。
変更を保存する
DataGridViewコントロールにデータベースのデータソースをバインドした場合、DataGridViewコントロールにおけるすべての変更がデータベースに自動的に反映されるわけではありません。そのため、すべての変更を手動でデータベースに戻す必要があります。
以前紹介した、DataGridViewコントロールにデータセットをバインドする例をもう一度考えます。次のコードは、データセットに「Customers」テーブルを読み込みます。
Dim connStr As String = _ "Data Source=.SQLEXPRESS;Initial Catalog=Northwind;" & _ "Integrated Security=True" Dim sql As String = "SELECT * FROM Customers" Dim conn As SqlConnection = New SqlConnection(connStr) Dim comm As SqlCommand = New SqlCommand(Sql, conn) Dim dataadapter As SqlDataAdapter = New SqlDataAdapter(comm) Dim ds As DataSet = New DataSet() ’---open the connection and fill the dataset--- conn.Open() dataadapter.Fill(ds, "Customers_table") conn.Close() DataGridView1.DataSource = ds DataGridView1.DataMember = "Customers_table"
DataGridViewコントロールでの変更をデータベースに戻すには、次のようにします。
Dim sqlCmdBuilder As New SqlCommandBuilder(dataadapter) sqlCmdBuilder.GetUpdateCommand() dataadapter.Update(ds.Tables("Customers_table"))
SqlCommandBuilderオブジェクトのGetUpdateCommand()メソッドによって、DataGridViewコントロールの変更を反映するSQLステートメントを自動的に作成します。その後、SqlDataAdapterオブジェクトにデータベース内のテーブルを更新させます。
型指定されたデータセットがDataGridViewコントロールに以下のような方法でバインドされている場合、
Dim adapter As New CustomersTableAdapter Dim bindingSrc As New BindingSource bindingSrc.DataSource = adapter.GetData DataGridView1.DataSource = bindingSrc
次のコードで変更をデータベースに簡単に反映させることができます。
bindingSrc.EndEdit() adapter.Update(bindingSrc.DataSource)
変更が文字列などに保存されている場合は、DataGridViewコントロール内のすべての行と列をループで処理します。
Dim output As String = String.Empty For Each row As DataGridViewRow In DataGridView1.Rows For Each cell As DataGridViewCell In row.Cells output += cell.Value & ":" Next output += vbCrLf Next MsgBox(output)
おわりに
本稿では、DataGridViewコントロール関連の一般的なタスクをどのように実行すればよいかを考察しました。DataGridViewは非常に用途の広いコントロールです。本稿が、DataGridViewを使用してデータベースやその他のデータソースを表示しようと考えているプログラマのリファレンスとして役立てば幸いです。
著者紹介
国際的なカンファレンスでたびたび講演し、.NET、XML、ワイヤレステクノロジーに関する著書、共著書多数。.NETからMac OS Xに至るまで広範な分野について執筆している。著書に『.NET Compact Framework Pocket Guide』 (Oreilly&Associates Inc、2005年5月)、『ASP.NET 2.0: A Developer’s Notebook』 (Oreilly&Associates Inc、2005年6月)、『Programming Sudoku』 (Apress刊、2006年3月)など。ブログ「Wei-Meng Lee’s Blog」を開設している。










