DataGridViewについて勉強しています

タグの編集
投稿者 のの  (社会人) 投稿日時 2020/9/7 11:33:05
お世話になります。

DataGridViewについて勉強しています。
テキストボックスに値を入れてボタンを押したら、テーブル内にその値があるか
確認したい場合、どうしたらいいのでしょうか?

その値があったらメッセージボックスで列と行と値がみたいです。

    Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click

        Dim Data As String
        Dim strText As String = TextBox1.Text


        For Each c As DataGridViewCell In DataGridView1.SelectedCells★
            Data = Me.DataGridView1(c.ColumnIndex, c.RowIndex).Value
            MsgBox("列" & c.ColumnIndex + 1 & "と" & "行" & c.RowIndex + 1 & "に存在しています値は" & Data & "です")
        Next c


    End Sub
End Class
投稿者 るきお  (社会人) 投稿日時 2020/9/7 19:42:58
たとえば、次のような感じになります。

Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click

    Dim strText As String = TextBox1.Text

    For Each row As DataGridViewRow In DataGridView1.Rows
        For Each c As DataGridViewCell In row.Cells
            Dim Data As String = c.Value?.ToString
            If Data = strText Then
                'Visual Basic 2013以前の場合 
                'MsgBox("列" & c.ColumnIndex + 1 & "と" & "行" & c.RowIndex + 1 & "に存在しています値は" & Data & "です") 

                'Visual Basic 2015以降ならこう書くほうが楽 
                MsgBox($"列{c.ColumnIndex + 1}と行{c.RowIndex + 1}に存在しています値は{Data}です")
            End If
        Next
    Next
End Sub



DataGridViewは複数の行(DataGridViewRow)から構成されます。それぞれの行は複数のセル(DataGridViewCell)から構成されます。
このような2重構造になっているので、ループで値を検索と二重ループが必要です。
投稿者 (削除されました)  () 投稿日時 2020/9/8 12:13:07
(削除されました)
投稿者 のの  (社会人) 投稿日時 2020/9/8 12:14:36
るきお様
返信ありがとうございます
2重ループが必要だったのですね!
初心者なのでコードも書いてくださり大変助かります。mom
参考にしてフォームつくります。


あと、もう一つわからないことがあって、、、教えてほしいのですが、書籍をみてデータセットによるデーターベースプログラムの作成をやっていまして、ある値だけ抜き出して欄に残すことをやりたいのですが
そういう場合はコードに接続文字を新たにセットしてSQLで構文をかいてTableDataGridViewにセットするのでしょうか?

よろしくおねがいします。
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2020/9/8 13:18:27
> 新たにセットしてSQLで構文をかいてTableDataGridViewにセットするのでしょうか?

そういう方法を取ることもできますが、既に DataTable にデータを抽出済みであれば、
そこから絞り込むという手も使えます。

既に DataSet/DataTable に取得済みの内容のうち、一部の行だけを表示させたい場合には、
BindingSource の Filter プロパティ あるいは DataView の RowFilter プロパティを使います。

たとえば、数学または英語が赤点だった生徒を列挙するために
 BindingSource1.Filter = "数学<=40 OR 英語<=40"
のように記述することができます。

ソートやフィルター、イベント処理などの制御が必要な場合には、
DataGridView1.DataSource に指定するのは、DataSet や DataTable ではなく、
BindingSource もしくは DataView の方が扱いやすいと思います。
BindingSource はデザイン時に割り当てておくこともできます。
※BindingSource1 の DataSource に DataTable を割り当てておき、
※DataGridView1 の DataSource に、BindingSource1を割り当てるようにする。

Visual Studio のサーバーエクスプローラーからドラッグ & ドロップで
フォームにテーブルを貼った場合も、DataGridView と共に BindingSource が貼られますね。


とはいえ、現在 BindingSource を現在使っておらず、
DataSource に DataSet や DataTable を直接割り当てていたとしても、
必ずしも BindingSource を使わなければいけないわけではありません

ソート処理やフィルター処理のためには、DataTable の DefaultView プロパティが返す
DataView オブジェクトの Sort / RowFilter プロパティを使って同じことができるためです。


なお、DataGridView の DataSource を用いている場合には、
> Data = Me.DataGridView1(c.ColumnIndex, c.RowIndex).Value
のような、行番号・列番号に依存したコードを書くことはお奨めしません。

列ヘッダーをクリックするなどしてソート処理を行うと、DataTable 上の行番号と
DataGridView 上の行番号は一致しなくなるためです。
このため、バインド元の DataRow を通じて読み書きする方が安全ですし、
パフォーマンスも向上します。

 For Each gridRow As DataGridViewRow In DataGridView1.Rows
  Dim rowView = DirectCast(row.DataBoundItem, DataViewRow)
  Dim row As DataRow = rowView.Row   '型付DataSetの場合は、型付DataRowで受け取れる 
    row("氏名") = "のの"



また、DataGridView 上で編集中の値は、他の行にカーソル移動するなどして
行データを確定するまでは、DataRow に反映されない事にも注意が必要です。
投稿者 のの  (社会人) 投稿日時 2020/9/8 15:36:17
魔界の仮面弁士様 
ご返信ありがとうございます。


>そういう方法を取ることもできますが、既に DataTable にデータを抽出済みであれば、
 そこから絞り込むという手も使えます。

すでにデーターはデーターソースからドラック&ドロップで
TableDataGridViewにはTableBindingSourceが割り当てられています。

>既に DataSet/DataTable に取得済みの内容のうち、一部の行だけを表示させたい場合には、
 BindingSource の Filter プロパティ あるいは DataView の RowFilter プロパティを使います。
BindingSourceはTableBindingSourceと一緒なのでしょうか?
なんかアイコンが一緒なので、、なまえが違うだけですか?

自分の割り当てはTableDataGridViewにTableBindingSourceという割り当てがされていて
品番という列に該当する値(テキストボックス入力)があった場合

    Private Sub Button3_Click_1(sender As Object, e As EventArgs) Handles Button3.Click
  Dim strText As String = TextBox1.Text
  TableBindingSource.Filter = strText

みたいな感じでいいのでしょうか?(ハンドルされてない例外が出てきました)


>Visual Studio のサーバーエクスプローラーからドラッグ & ドロップで
フォームにテーブルを貼った場合も、DataGridView と共に BindingSource が貼られますね。
とはいえ、現在 BindingSource を現在使っておらず、
DataSource に DataSet や DataTable を直接割り当てていたとしても、
必ずしも BindingSource を使わなければいけないわけではありません
ソート処理やフィルター処理のためには、DataTable の DefaultView プロパティが返す
DataView オブジェクトの Sort / RowFilter プロパティを使って同じことができるためです。

いろんなやり方があるのですね!
おもったより奥がふかいです。。

なお、DataGridView の DataSource を用いている場合には、
> Data = Me.DataGridView1(c.ColumnIndex, c.RowIndex).Value
のような、行番号・列番号に依存したコードを書くことはお奨めしません。

なるほど、、
はじめてDataGridViewをつかうので気を付けたいとおもいます
コード参考にさせていただきます。

よろしくおねがいします。

投稿者 魔界の仮面弁士  (社会人) 投稿日時 2020/9/8 16:59:33
掲示板の IMAGE タグで https プロトコルをサポートして頂けないでしょうか。 > るきおさん


> BindingSourceはTableBindingSourceと一緒なのでしょうか?
もしかして、【型名】と【フィールド変数名】を混同していませんか?

恐らくは System.Windows.Forms.BindingSource クラスのインスタンスの Name プロパティに
"TableBindingSource" という名前が付けられている状態だと思います。

System.Windows.Forms.TextBox クラスのインスタンスの Name プロパティに
"TextBox1" や "TextBox2" といった名前が付けられるのと同じように。




> TableBindingSource.Filter = strText
> みたいな感じでいいのでしょうか?(ハンドルされてない例外が出てきました)
いいえ。SQL でいうところの WHERE 句に書く問い合わせ式が必要です。

先ほどは "数学<=40 OR 英語<=40" という文字列を例に挙げましたが、この場合、
DataTable 上に "数学" 列と "英語" 列があり、かつそれらが数値型であることを想定しています。

Filter プロパティに設定できる文字列は、DataColumn の Expression プロパティに
指定できる内容に準じます。たとえば LIKE 検索なども可能です。

具体的にな構文については、下記を参考にしてみてください。
(翻訳ミスで構文が崩れている箇所があるので、英語版も併記しておきます)
https://docs.microsoft.com/ja-jp/dotnet/api/system.data.datacolumn.expression?WT.mc_id=DT-MVP-8907&view=netframework-4.8
https://docs.microsoft.com/en-us/dotnet/api/system.data.datacolumn.expression?WT.mc_id=DT-MVP-8907&view=netframework-4.8


あるいは、DataTable の Select メソッドを使って、DataRow の配列を得る方法もあります。
使い方はほぼ同じですが、抽出結果の DataRow 配列を DataGridView 等に表示したいなら、
そのための再割り当て等は、自分で行う必要があります。

このほか、LINQ to DataSet で問い合わせるといった方法があります。
https://docs.microsoft.com/ja-jp/dotnet/framework/data/adonet/creating-a-datatable-from-a-query-linq-to-dataset?WT.mc_id=DT-MVP-8907
投稿者 のの  (社会人) 投稿日時 2020/9/9 11:59:22
魔界の仮面弁士様
ありがとうございます。

恐らくは System.Windows.Forms.BindingSource クラスのインスタンスの Name プロパティに
"TableBindingSource" という名前が付けられている状態だと思います。
System.Windows.Forms.TextBox クラスのインスタンスの Name プロパティに
"TextBox1" や "TextBox2" といった名前が付けられるのと同じように。

>点と点がつながりました。
自分勘違いしていました。

いいえ。SQL でいうところの WHERE 句に書く問い合わせ式が必要です。
>リンクみました。ありがとうございます。

テキストボックスの値を品番という列で検索してフィルターしたいのですが、(全部str型です)
  TableBindingSource.Filter = "品番 = '*name' '*"
の"name" を strText (変数)に置き換えたいのですがどうやるかわかりません。。おしえてください。

宜しくおねがいします。
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2020/9/9 13:22:18
御自身の発言の直前に全角「>」を挿入するスタイルは、ちょっと読みにくいです。

相手の発言を引用する場合は、行頭に半角の「>」を挿入してください。
この掲示板では、引用文が青文字で表示されるようになっています。


> TableBindingSource.Filter = "品番 = '*name' '*"
> の"name" を strText (変数)に置き換えたいのですがどうやるかわかりません。。

TableBindingSource.Filter = "品番 = '" & strText & "'"

ですね。VB2015 以降をお使いであれば、このようにも書けます。

TableBindingSource.Filter = $"品番 = '{strText}'"


実際にはインジェクション対策も必要なことに注意しましょう。
(たとえば、strText 内に「'」の文字が含まれていた場合の対策を行う、ということです)



ところで……品番ということは恐らく、主キー項目もしくは外部キー項目だと思います。

もし、外部キーではなく主キーであるなら、"品番" 列を
その DataTable の PrimaryKey プロパティに割り当てておくと良いでしょう。
(型付 DataSet の場合は、品番列の上で右クリックして、「主キーの設定」を行う)


PrimaryKey が "品番" 列だけを指し示している場合、品番検索の結果は
複数行にはならず、特定の行を指し示すことになりますが、この場合は
下記のような処理も行えます。(この方法では、インジェクション対策が不要です)

Dim row As DataRow = データテーブル.Rows.Find(strText)


型付DataSet を使っている場合には、このように書けます。

Dim row As データセット名.表名Row = データテーブル.FindBy品番(strText)
投稿者 のの  (社会人) 投稿日時 2020/9/9 15:55:51
>御自身の発言の直前に全角「>」を挿入するスタイルは、ちょっと読みにくいです。
>相手の発言を引用する場合は、行頭に半角の「>」を挿入してください。
>この掲示板では、引用文が青文字で表示されるようになっています。

なんで青くなっているのかとおもったら 「>」 だったのですね
教えて下りありがとうございます つぎから気をつけます
コードをいろいろいじっていたら気づきがたくさんありました。
どうすればいいかこまっていたのでとっても助かりました。

>ところで……品番ということは恐らく、主キー項目もしくは外部キー項目だと思います。
鍵マークのことですよね
初心者なものでそこまで気が付けなくて、、言ってもらえて助かりました。PrimaryKey
に設定しました。

>Dim row As データセット名.表名Row = データテーブル.FindBy品番(strText)
ちなみになのですがデーターセット名や表名 データーテーブル名はどこを見れば確認できますでしょうか?
自分でデータセット作っておきながら右も左もわからなくてすいません。。

宜しくお願いします。