|
事業仕分けによる次世代スーパーコンピューターの開発予算削減について、どうお考えですか?
|
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 図2 文字列配列をDataGridViewコントロールにバインドした結果 ![]() この結果は意外なものかもしれません。おそらく、配列の各要素が表示されると予想していた人が多いのではないでしょうか。実際には、各要素の長さが表示されます。DataGridViewコントロールはバインドされたオブジェクトの最初のパブリックプロパティを探すからです。この例では、文字列配列の最初のパブリックプロパティは配列内の各要素の長さなので、上のような結果が得られるわけです。 文字列配列をDataGridViewコントロールに正しくバインドするためには、次に示すように文字列型をラップするクラスを作成し、このクラスが、要素の内容を返すパブリックプロパティ 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 次の例では、 ’---modified array--- Dim arr() As CStringItem = { _ New CStringItem("Product 1"), _ New CStringItem("Product 2"), _ New CStringItem("Product 3")} DataGridView1.DataSource = arr 図3は、このデータバインドの結果です。パブリックプロパティ 図3 DataGridViewコントロールにオブジェクトの配列をバインドした結果 ![]() カスタムオブジェクトをバインドする 次の例では、DataGridViewコントロールにもっと複雑なオブジェクトをバインドする方法を説明します。2つのパブリックプロパティ( 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コントロールに ’---binding to custom object--- Dim students() As CStudent = _ {New CStudent("123-456-789", "John"), _ New CStudent("456-123-789", "Mary")} DataGridView1.DataSource = students 図4 DataGridViewコントロールにCStudentオブジェクトの配列をバインドした結果 ![]() 型指定されたデータセットを用いてバインドする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は、このデータバインドの結果です。 データセットでバインドする データセットを手動で作成する場合は、 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コントロール内のセルの値を取得する場合は、 ’---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 DataViewでフィルタリングする DataGridViewコントロールに表示されるデータにフィルタをかけるには ’---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 上の例は、 上の例では、行をContactTitleフィールドの昇順でソートし、さらにAddressフィールドの昇順でソートしています。Addressフィールドの降順でソートする場合は、次のように設定します。
.Sort = "ContactTitle ASC, Address DESC"
図7に、ContactTitleフィールドとAddressフィールドを共に昇順でソートしたケースと、ContactTitleを昇順、Addressを降順でソートしたケースを示します。 列をソートする
’---sort based on the first column---
DataGridView1.Sort(DataGridView1.Columns(0), _
System.ComponentModel.ListSortDirection.Descending)
この例では、行を最初の列の降順でソートしています(図8)。 プログラムによるソートとは別に、ユーザーが実行時にソートグリフ(列見出しの隣に表示される三角形のアイコン)をクリックして行を昇順または降順にソートすることも可能です。行がソートされないようにするには、
’---set the sort mode for a column---
DataGridView1.Columns(1).SortMode = _
DataGridViewColumnSortMode.NotSortable
プログラミングによって列を追加する これまで紹介した方法は、データセットまたはBindingSourceコントロールを用いて、DataGridViewコントロールにデータベースをバインドするというものでした。しかし、ときにはプログラミングによってDataGridViewコントロール内にさまざまなフィールドを作成し、ユーザーに値を設定させる場合もあります(同様に、プログラミングによって新しい行を追加することもできます)。DataGridViewコントロール内に列を作成するには、DataGridViewコントロールの ’---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コントロールの表示結果です。 図9 DataGridViewコントロールに4つのフィールドを作成する ![]() 行を追加する DataGridViewコントロールに行を追加するには、
’---add an empty row---
DataGridView1.Rows.Add()
行を追加してそこに内容を割り当てるには、 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コントロールの 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コントロールの 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になることに注意してください。 行/列をロックする 行の ’---first row is readonly--- DataGridView1.Rows(0).ReadOnly = True 同様に、特定の列に属するセルをロックすることもできます。 ’---first column is readonly--- DataGridView1.Columns(0).ReadOnly = True 必要ならDataGridViewコントロール全体をロックすることもできます。 ’---entire grid is readonly--- DataGridView1.ReadOnly = True 列を非表示にする 実行時に列の ’---hides the second column--- DataGridView1.Columns(1).Visible = False 図13に、列を非表示にしたDataGridViewの例を示します。奥はすべての列を表示した状態で、手前は2番目の列(CompanyName列)を非表示にした状態です。 ユーザーの編集を検証するDataGridViewコントロールの一般的な用途の1つはデータ入力です。ユーザーはコントロールの個々のセルにデータを入力します。そのため、正しい種類のデータが入力されたかどうかを検証することが重要です。 入力されたデータの種類が正しいことを検証するには、2つのイベントを処理する必要があります。最初に 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コントロールの一番左の列にエラーメッセージを表示します( ユーザーはエラーを訂正しないと他のセルに移動できません。訂正データが入力されたときにエラーメッセージが消えるようにするには、 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コントロール内の行を削除するには、 For Each row As DataGridViewRow In DataGridView1.SelectedRows DataGridView1.Rows.Remove(row) Next ユーザーは最初に行を選択して[Delete]キーを押す方法でも行を削除できます。デフォルトで、この削除は確認なしに自動で実行されます。しかし、削除の前にユーザーに確認を求めることもできます。これには 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")) 型指定されたデータセットが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を使用してデータベースやその他のデータソースを表示しようと考えているプログラマのリファレンスとして役立てば幸いです。 著者紹介Wei-Meng Lee(Wei-Meng Lee)
Microsoft MVP受賞者。Microsoft社の最新テクノロジー実地研修を専門とするDeveloper Learning Solutions社を創設。.NETとワイヤレステクノロジーの開発者、指導者として知られる。
国際的なカンファレンスでたびたび講演し、.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」を開設している。 |