Visual Basic 中学校 掲示板 投稿の管理
タグのない投稿を抽出
統計
RSS
Visual Basic 中学校
投稿一覧
選択行の削除
この投稿へのリンク
https://keijiban.umayadia.com/ThreadDetail.aspx?ThreadId=30267#CommentId82945
この投稿の削除
削除パスワード
削除する
コメント本文
投稿者
魔界の仮面弁士
 (社会人)
投稿日時
2017/8/1 19:13:46
最初の質問では WHERE ITEM_ID = ~ だったのに
今回のコードは WHERE NUMBER = ~ ですね。
どちらが正しいのでしょうか。
> 1行しか削除されませんでした。
それは、CurrentCell や CurrentRow を使っているからですね。
> 複数行でも削除できるようにするにはfor文などを使用するのが正しいですか?
DataGridView 上の複数行を取得する方法は、既に具体例付きで回答しています。
すなわち
【行全体が選択されているものを対象とする場合】
【現在のカーソル行とは関係なく、選択されているセルの行を対象とする場合】
の 2 つのコードの事です。For Each のループ処理が書かれていましたよね?
> 複数行選択された状態で削除ボタンが押下された場合、
そもそも「選択された行」と「現在の行」は無関係です。
たとえば下記の図の場合、このような状態を意味します。
CurrentCell は 1 件。[行1列0]
CurrentRow は 1 件。[行1]
SelectedRows は 2 件。[行3],[行4]
SelectedCells は 5 件。[行3列0],[行3列1],[行4列0],[行4列1],[行6列1]
さてこの場合、削除したいのは以下のいずれでしょうか?
(A) 1 件削除。行1 のみ。CurrentRow を使う。(現状がこれですよね)
(B) 2 件削除。行3 と 行4。SelectedRows を使います。
(C) 3 件削除。行3 と 行4 と 行6。SelectedCells を集約してから使います。
これらの 3 パターンの行取得の手順は、先の投稿を参照してください。
A ならば【現在のカーソル行を対象とする場合】の手法です。
B ならば【行全体が選択されているものを対象とする場合】です。
C ならば【選択されているセルの行を対象とする場合】です。
SelectionMode に FullRowSelect を指定している場合は、
C は使わないので B 案になるでしょう。
先の例においては、削除のために「Delete メソッド」を呼び出していたわけですが、
今回は Delete メソッドを呼ぶ代わりに、ソッケルさんの「DELETE SQL を呼ぶ」コードを
使うことで、ひとまずの目的を達することができるでしょう。
(B あるいは C の For Each の中の処理を書き換える、ということです)
もう少し書いておくと……ソッケルさんの
> DataGridView1.CurrentRow.Cells("注文番号").Value
の場合、「DataGridView1.CurrentRow」が DataGridViewRow 型を表しますよね。
一方、先の例の
> For Each gridRow As DataGridViewRow In DataGridView1.SelectedRows
の場合、「gridRow」が DataGridViewRow 型で得られますので、
gridRow.Cells("注文番号").Value
のようにして、各行の注文番号を得られることになるわけです。
あとは同じように書き換えられますよね。
さて、これで複数件の削除はひとまず行えるのですが、
さらに追記しておくべきことがあります。
> Dim delete As String = "DELETE FROM T_ITEM WHERE NUMBER = '" & DataGridView1.CurrentRow.Cells("注文番号").Value & "'"
残念ながら、このコードには 2 つの問題があります。
一つは、データがゼロ件だった場合などのように、
CurrentRow が Nothing を返してくる場合の対処が漏れていること。
もう一つは、SQL インジェクションが考慮されていない点です。
後者は特に危険なので、是正するべきです。
たとえば提示頂いた例で言えば、注文番号欄に
「'--」あるいは「' OR ''='」が入力されることで
T_ITEM 上の全データが削除される危険性を含んでしまいます。
どうするべきかというと、値を埋め込んだ SQL コマンドを直接作るのではなく、
前回の最後に書いた【DELETE 文を手動で生成する場合】のサンプルコードのように、
パラメーター付きの SQL にするということです。
MySQL の場合は、「WHERE NUMBER = ?」の無名パラメーターと
「WHERE NUMBER = @PARAM1」などの名前付きパラメーターの
両方の構文に対応しています。あとは、そのパラメーターに対応させて
MySqlParameter オブジェクトを用意して渡すようにすれば、
『'』が含まれるような文字列であっても、安全に引き渡すことができます。
もし、どうしても値を直接 SQL に埋め込まなくてはならないのなら、
データ内容が安全かを事前にチェックするコードを設けておくか、
あるいは、必ずサニタイジングを施す必要があります。たとえば、
文字列中の「'」や「"」を、Replace メソッドを使うなどして
「\'」や「\"」に置き換えるといった手法です。
https://dev.mysql.com/doc/refman/5.6/ja/string-literals.html
とはいえ、このような手動サニタイズは作業漏れを引き起こしやすいので、
可能な限り、MySqlParameter あるいは MySqlDataAdapter を
使うことを心がけておいたほうが安全です。
> Dim mycomand As New MySqlCommand(delete, conn)
mycomand ではなく
mycommand かな…。
> DataGridView1.Rows.RemoveAt(DataGridView1.CurrentCell.RowIndex)
わざわざ行番号に戻さずとも、RemoveAt の代わりに Remove を使うようにすれば
DataGridView1.Rows.Remove(DataGridView1.CurrentRow)
のように、行オブジェクトをそのまま渡せますよ。
> Catch ex As Exception
> MsgBox(ex.Message)
確認では MessageBox.Show を用いていたのに、
エラー表示は MsgBox なんですね。
何か理由があって使い分けているのでしょうか?
> '合計の表示
> sum()
以下、蛇足情報として。
上記がどのような処理で実装されているか分かりませんが、
現在表示されている内容の合計を求めるのであれば、
DataTable の Compute メソッドを使うと、
ループ処理を使わずに、合計を簡単に求められます。
あるいは VB の Aggregate 句を使う方法などもあります。
蛇足ついでにもう一つ。ちょっと難しい内容かもしれませんが、
データ量が多くなってきた場合に必要となる情報を紹介しておきます。
[Windows フォーム DataGridView コントロールを拡張するための推奨される手順]
https://msdn.microsoft.com/ja-jp/library/ha5xt0d9.aspx