フリーズしてる訳でもないんだから、プログレスバーでも出しておけば良いんじゃないの?と書いておきながら、非同期処理でUIにプログレスバーを表示してみる事に。
矢張り曲者は、別スレッドからのUIコントロール更新でしょうか。(デリゲートメソッドもコールバックメソッドも、スレッドプールで処理を実行)
【デリゲートメソッドの終了待ち】
・BeginInvoke呼出しメソッドの終了をEndInvokeで受けるのですが、UI(メイン)スレッド中に EndInvoke を呼び出すと非同期(デリゲート)メソッドが完了するまで、呼出し元スレッドがブロックされてしまいます。*1
・UIサービスを提供するスレッドからは、EndInvoke を呼び出さない方が宜しいかと。⇒ コールバックメソッドの利用
*1:IDEのデバッグ実行では、飛んだまま戻ってこず
<注意点>
・仮引数の範囲のチェック(0%~100%のプログレスバー設定値)や何処でチェックするかなどは、多分に好みの問題だと思うので・・・。
・エラー処理は手抜きです。
お試し環境
Windows7 64bit Edition
Visual Basic 2008 AnyCPU対象
/*-------------------------------- お試し結果 ------------------------------*/
・ボタン1を押すと、
・ボタン2を押すと、
/*----------------------------------------------------------------------------*/
/*---------------------------- お試しソース -------------------------------*/
Public Class Form4
Private Delegate Sub dlgtWork1Prgbar1(ByVal data As Integer)
Private Sub Set1Prgbar1Value(ByVal percent As Integer)
If InvokeRequired = True Then
Invoke(New dlgtWork1Prgbar1(AddressOf Set1Prgbar1Value), percent)
Else
ProgressBar1.Value = percent
If percent >= 100 Then
Label1.ForeColor = System.Drawing.Color.Red
Label1.Text = "完了"
Button1.Enabled = True
End If
End If
End Sub
Private Sub work1PrgBar1(ByVal no As Integer)
For i As Integer = no To 10
System.Threading.Thread.Sleep(500) ' 時間の掛かる処理の代替
Set1Prgbar1Value(i * 10)
Next
End Sub
Private Sub bar1Callback1(ByVal ar As IAsyncResult)
Dim arWk1Prgbar1 As dlgtWork1Prgbar1
arWk1Prgbar1 = CType(ar.AsyncState, dlgtWork1Prgbar1)
arWk1Prgbar1.EndInvoke(ar)
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim wk1PrgBar1 As dlgtWork1Prgbar1
Dim ar As IAsyncResult
Button1.Enabled = False
Label1.ForeColor = System.Drawing.Color.FromKnownColor(KnownColor.ControlText)
Label1.Text = "実行中・・・"
Set1Prgbar1Value(0)
wk1PrgBar1 = New dlgtWork1Prgbar1(AddressOf work1PrgBar1)
ar = wk1PrgBar1.BeginInvoke(1, New AsyncCallback(AddressOf bar1Callback1), wk1PrgBar1)
End Sub
/*-------------------------- お試しソース 改 -----------------------------*/
Structure prgrsData
Dim percent As Integer
Dim leftTm As TimeSpan
End Structure
Private Delegate Sub dlgtWork2Prgbar1(ByVal data As prgrsData)
Private Sub Set2Prgbar1Value(ByVal prgDat As prgrsData)
If InvokeRequired = True Then
Invoke(New dlgtWork2Prgbar1(AddressOf Set2Prgbar1Value), prgDat)
Else
ProgressBar1.Value = prgDat.percent
Label1.Text = String.Format("あと {0:0.0} 秒・・・", prgDat.leftTm.TotalSeconds)
If prgDat.percent >= 100 Then
Label1.ForeColor = System.Drawing.Color.Red
Label1.Text = "完了"
Button2.Enabled = True
End If
End If
End Sub
Private Sub work2PrgBar1(ByVal prgDat As prgrsData)
Const Cntr As Integer = 10
Const MSec As Integer = 500
Dim pData As prgrsData
For i As Integer = prgDat.percent To Cntr
System.Threading.Thread.Sleep(MSec) ' 時間の掛かる処理の代替
pData.percent = i * Cntr
pData.leftTm = TimeSpan.FromMilliseconds((Cntr - i) * MSec)
Set2Prgbar1Value(pData)
Next
End Sub
Private Sub bar1Callback2(ByVal ar As IAsyncResult)
Dim arWk2Prgbar1 As dlgtWork2Prgbar1
arWk2Prgbar1 = CType(ar.AsyncState, dlgtWork2Prgbar1)
arWk2Prgbar1.EndInvoke(ar)
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Dim wk2Prgbar1 As dlgtWork2Prgbar1
Dim ar As IAsyncResult
Dim progDat As prgrsData
Button2.Enabled = False
Label1.ForeColor = System.Drawing.Color.FromKnownColor(KnownColor.ControlText)
Label1.Text = "実行中・・・"
progDat.percent = 0
progDat.leftTm = TimeSpan.FromMilliseconds(5000)
Set2Prgbar1Value(progDat)
progDat.percent = 1
wk2Prgbar1 = New dlgtWork2Prgbar1(AddressOf work2PrgBar1)
ar = wk2Prgbar1.BeginInvoke(progDat, New AsyncCallback(AddressOf bar1Callback2), wk2Prgbar1)
End Sub
End Class
/*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
/*============================================================================*/