tagCANDY CGI VBレスキュー(花ちゃん) の Visual Basic 2010 用 掲示板(VB.NET 掲示板)
VBレスキュー(花ちゃん) の Visual Basic 2010 用 掲示板(VB.NET 掲示板)
[ツリー表示へ]  [ワード検索]  [Home]

タイトル マルチスレッドでコントロールを操作する方法
投稿日: 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

- 関連一覧ツリー をクリックするとツリー全体を一括表示します)

古いスレッドにレスはつけられません。