2013年4月18日木曜日

VB BackgroundWorker を独立したフォームに配置

先日、msdn の VB Forum で、複数のフォームから共通した BackgroundWorker を呼び出す方法を投稿しました。
基本的なコードのみの掲載で、一部は省略もしている為、ここにもう少し説明したいと思います。



BackgroundWorker は、時間のかかる処理を別スレッドにしてバックグラウンドで実行させる機能 のひとつですね。
これを使う事で、処理中に UI ( User Interface ) が応答しなくなる事を避けられる様になります。
作ったアプリが移動や最小化すらできない状態になる なぁんて事から、開放されます。

 正直に書きます。 今迄、使った事ありませんでした ... 。
 VB で作ってるのは自分もしくは身近な人の為のアプリなのと、今迄扱った重い処理 イコール クリティカルな処理で
 敢えて応答させなくしていた程です。 ( Me.ControlBox = False で閉じる事もさせなくしてた位です。 )
 それと、見ていた説明が いまひとつ分り難かった という事もあります ... 。 食わず嫌い ... 。
 でも、やはり、便利な機能は使わなくっちゃ~ という訳で 本題に。

よくある BackgroundWorker の説明は 呼出元のフォームに配置し、そこに各種のコントロールも置くやり方です。 
処理の実行開始は ボタンを押し下げる 事で、処理の重複実行(エラー)を避ける為に ボタンの有効無効を切り替え ています。
ただし、ひとつの処理を 複数のフォームから呼び出す場合は これは面倒です。

そこで、BackgroundWorker の処理を扱うフォームを 独立させて これを呼び出し 表示と共に処理を開始する様に プログラミングして見ました。
Public Class FormBGW
    Private Sub FormBGW_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Me.BackgroundWorker1.WorkerSupportsCancellation = True
        Me.BackgroundWorker1.WorkerReportsProgress = True
    End Sub
    Private Sub FormBGW_Shown(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Shown
        ' Whenever This Form Is First Displayed
        Me.BackgroundWorker1.RunWorkerAsync(100)    ' 処理開始
    End Sub
    Private Sub ButtonCancel_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ButtonCancel.Click
        If Me.BackgroundWorker1.IsBusy Then
            Me.BackgroundWorker1.CancelAsync()
            MsgBox("Canceled !")
            Me.Close()
        End If
    End Sub
    Private Sub BackgroundWorker1_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
        ' このメソッドへのパラメータ
        Dim bgWorkerArg As Integer = CType(e.Argument, Integer)
        Dim worker As System.ComponentModel.BackgroundWorker = CType(sender, System.ComponentModel.BackgroundWorker)
        ' 時間のかかる処理
        For i As Integer = 1 To bgWorkerArg
            ' キャンセル取得
            If Me.BackgroundWorker1.CancellationPending Then
                e.Cancel = True
                Return
            End If
            System.Threading.Thread.Sleep(200)
            Dim percentage As Integer = i * 100 / bgWorkerArg ' 進ちょく率
            worker.ReportProgress(percentage)
            ' ProgressChangedイベント発生
        Next
        ' このメソッドからの戻り値
        e.Result = "すべて完了"
        ' この後、RunWorkerCompletedイベントが発生
    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.LabelMsg.Text = "Canceled!"
        ElseIf e.Error IsNot Nothing Then
            Me.LabelMsg.Text = "Error: " & e.Error.Message
        Else
            Me.LabelMsg.Text = "Done!"
            Me.Close()
        End If
    End Sub
    Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
        Me.Text = e.ProgressPercentage & "%完了"
        Me.ProgressBar1.Value = e.ProgressPercentage
        Me.LabelMsg.Text = e.ProgressPercentage.ToString() & "%"
    End Sub
End Class
msdn フォーラムで示したコードの省略部分も全て入っています。 実行可。
コードの前半がフォームに用意したコントロール部分で 後半が BackgroundWorker の部分です。
BackgroundWorker1_ で始まるサブルーチン群が後半です。
VB / Visual Studio のツールボックスからコンポーネントを持ってくる前提のコードですので、
BackgroundWorker の初期化や宣言文は、VB が自動で補っていますね。
中断を盛り込んでも、3 つのサブルーチン( _DoWork,_RunWorkerCompleted,_ProgressChanged )で準備が出来てしまいます。
処理の開始は、このフォームが最初に表示された時。 サブルーチン FormBGW_Shown で指定しています。
従って、処理を呼び出すのは 他のフォームから FormBGW.Show() するだけです。
フォームが表示されている つまり 処理実行中は 別のフォームから呼び出されても 重複実行されません。






0 件のコメント:

コメントを投稿