BackgroundWorker 重たい処理のキャンセル 2005.NET


VB Tips And Sample(HOME)(VB.NET Sample インデックス)

重たい処理がキャンセルできるようにするには、Application.DoEvents()をかまして行う方法があるが、
BackgroundWorkerコントロールができたのでこれを使う方法も選べるようになった。

BackgroundWorkerの良い点。
その名の通りバックグランドで行われるので実行中にフォームをクリックすると実行が止まる
ということがなくなり、よりスマートなUIが簡単に実現できるという点にあるようだ。

以下のサンプルはフォームにボタン2個、ラベル1個、
BackgroundWorkerコンポーネント1個(GUIからプロパティー 途中経過有効 キェンセル有効 をTrueにしたもの。)を追加して書いたもの

''Imports System.ComponentModel 'こいつをインポートしておいてもよい


Public Class Form1

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        'スタートさせる
        Me.Button1.Enabled = False '二重実行は厳禁なのでこうしておく
        BackgroundWorker1.RunWorkerAsync(1000) 'これで重たい処理がバックグランドで実行される。
        '1000は単なるパラメータ こうやって渡すことができる。

    End Sub

    Private Sub BackgroundWorker1_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
        'ここに重たい処理を書く
        'ここで実行される処理は バックグランドで行われるので このルーチンからユーザインターフェイスは操作できない
        'でも途中経過を知らなければバックグランドを使う意味がない。
        'でコントロールにProgressChangedイベントがあるというわけ


        'BackgroundWorker1.RunWorkerAsync(1000)の1000をiに取得する。
        'ArgumentはHelpには非同期操作の引数を表す値を取得します。と書いてある。おまじないだ。
        Dim i As Integer = CType(e.Argument, Integer)
        Dim j As Integer = 0

        'senderにバックグランドスレッド(オブジェクト)が入ってくるので取り出す。
        Dim back As System.ComponentModel.BackgroundWorker = CType(sender, System.ComponentModel.BackgroundWorker)


        Do Until j = i
            If back.CancellationPending = True Then
                'キャンセルボタンが押されたか調査
                e.Cancel = True
                Exit Do
            End If
            ' ''Application.DoEvents() 'これでは制御できない
            ' ''Application.DoEvents() 'これでは制御できない
            ' ''Application.DoEvents() 'これでは制御できない
            ' ''Application.DoEvents() 'これでは制御できない
            ' ''Application.DoEvents() 'これでは制御できない
            ' ''Application.DoEvents() 'これでは制御できない
            ' ''Application.DoEvents() 'これでは制御できない

            System.Threading.Thread.Sleep(10) '指定したミリ秒数の間現在のスレッドをブロックします。これを書かないとキャンセルボタンは押せない
            'ということはトータルの実行速度は遅くなる [1]だとキャンセルボタンは押せない 最小は[10]ぐらいか?
            '0を指定すると途中停止になるらしいが止まらない

            ''Me.Label1.Text = j & "%" 'これはNG "有効ではないスレッド間の操作: コントロールが作成されたスレッド以外のスレッドからコントロール 'Label1' がアクセスされました。" が出る
            back.ReportProgress(j) '何パーセント終了したか0〜で入れるらしいが100を超えてもエラーは起きない Integerの2,147,483,647まで 
            j += 1 'インクリメント
            Debug.WriteLine(j)

        Loop

        e.Result = "終了"
    End Sub

    Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
        '途中経過の情報が入ってくる
        Me.Label1.Text = e.ProgressPercentage & "%"
        Debug.WriteLine(Me.Label1.Text)
    End Sub

    Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
        'すべて終了したら入って来る
        If e.Cancelled = True Then
            Me.Label1.Text = "キャンセルしました"
        Else
            Me.Label1.Text = e.Result
        End If

    End Sub

    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        ’キャンセルボタン
        BackgroundWorker1.CancelAsync()

    End Sub
End Class



VB Tips And Sample(HOME)(VB.NET Sample インデックス)