タイトル | : マルチスレッドでコントロールを操作する方法 |
記事No | : 6297 |
投稿日 | : 2007/09/13(Thu) 15:58 |
投稿者 | : ちゃら |
環境 : Visual Basic 2005
実現したい処理: フォーム上に、 DataGridView1 と Button1 を配置。 フォーム起動時に、 DataGridView1 に50レコード追加し、表示。 Button1 を押すと、 DataGridView1 のレコードを上から 1行ずつ参照し、特定の処理を行う。 処理している行が分かるように、 その行の処理を開始した時、セルに "処理中..." と表示し その行の処理が終了した時、セルに "処理完了" と表示したい。
これを実現する為に次のようなコードを作成しました。(コードA参照)
しかし、これではシングルスレッドである為、 処理が完了するまで、何も反応しません。 DataGridView に "処理中..." などの状況も表示されません。
# MainTable.Rows(i)("column1") = "処理中..." # の直後に DataGridView1.Refresh を入れると # とりあえず、思っていた通りの処理にはなりますが、 # 処理が遅くなってしまいます。 # このサンプルでは、遅くなることを実感できないかもしれませんが・・・
そこで、過去スレなどを参照し、 非同期で実行できるように改良しました。(コードB参照) # Form_Load の処理は変更なしです。 とりあえず、思っていた通りの処理になったのですが、 コードBで問題ないでしょうか?
一番不安なのが、 BackgroundWorker1_DoWork の処理で、 Invoke メソッドって使用してしまってよいのでしょうか? またそのオブジェクトは Me にしていますが、それでよいのでしょうか?
================================================================================ '' (コードA)同期版 Public Class Form1 Dim MainTable As New System.Data.DataTable
' フォームロード時 Private Sub Form1_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load MainTable.Columns.Add("column1", System.Type.GetType("System.String")) For i As Integer = 0 To 50 - 1 Dim row As System.Data.DataRow = MainTable.NewRow row("column1") = "" MainTable.Rows.Add(row) Next DataGridView1.DataSource = MainTable End Sub
' 実行ボタン押下時 Private Sub Button1_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Button1.Click ' グリッドの初期化 For i As Integer = 0 To 50 - 1 MainTable.Rows(i)("column1") = "" Next ' 1行ずつ処理 For i As Integer = 0 To 50 - 1 ' DataGridView のセル値更新 MainTable.Rows(i)("column1") = "処理中..." ' 重たい処理(ここでは 0.1 秒待機) System.Threading.Thread.Sleep(100) ' DataGridView のセル値更新 MainTable.Rows(i)("column1") = "処理完了" Next MsgBox("処理が完了しました") End Sub End Class
'' (コードB)非同期版 Public Class Form1 Dim MainTable As New System.Data.DataTable
' 別スレッドからメインスレッドのプロシージャを呼ぶ為のデリゲート宣言 Private Delegate Sub DelegateGridRefresh(ByVal row As Integer, ByVal value As String)
' フォームロード時 Private Sub Form1_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load MainTable.Columns.Add("column1", System.Type.GetType("System.String")) For i As Integer = 0 To 50 - 1 Dim row As System.Data.DataRow = MainTable.NewRow row("column1") = "" MainTable.Rows.Add(row) Next DataGridView1.DataSource = MainTable End Sub
' 実行ボタン押下時 Private Sub Button1_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Button1.Click ' グリッドの初期化 For i As Integer = 0 To 50 - 1 MainTable.Rows(i)("column1") = "" Next ' 非同期処理の開始 BackgroundWorker1.RunWorkerAsync() End Sub
' 別スレッドの処理 Private Sub BackgroundWorker1_DoWork(ByVal sender As Object, _ ByVal e As System.ComponentModel.DoWorkEventArgs) _ Handles BackgroundWorker1.DoWork Dim worker As System.ComponentModel.BackgroundWorker = _ TryCast(sender, System.ComponentModel.BackgroundWorker) ' 1行ずつ処理 For i As Integer = 0 To 50 - 1 ' DataGridView のセル値更新 Me.Invoke(New DelegateGridRefresh(AddressOf GridRefresh), New Object() {i, "処理中..."}) ' 重たい処理(ここでは 0.1 秒待機) System.Threading.Thread.Sleep(100) ' DataGridView のセル値更新 Me.Invoke(New DelegateGridRefresh(AddressOf GridRefresh), New Object() {i, "処理完了"}) Next End Sub
' 別スレッド終了時 Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As Object, _ ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) _ Handles BackgroundWorker1.RunWorkerCompleted MsgBox("処理が完了しました") End Sub
' DataGridView のセル値変更プロシージャ Private Sub GridRefresh(ByVal row As Integer, ByVal value As String) MainTable.Rows(row)("column1") = value End Sub End Class
|