VB.NET でプログレスバーを非同期で動作させてみます。
VB.NET における非同期処理、キャンセル処理、Progress クラスの使い方等のサンプルになってます。C# の情報はたくさん見つかるけど、VB.NET のサンプルは少なくて苦労しますね。
この記事ではダイアログを WPF で作ってますが、 Forms で作ったとしても、プログラムコードは共通で使えると思います。
下のビデオでは、プログレスバーが非同期で動いていることを示すために、プログレスバーの動作中にダイアログを動かしたりサイズ変更したりしています。また、キャンセルのテストもしています。
XAML
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="135" Width="392">
<Grid>
<TextBlock Height="23" Name="progressMsg" Text="進行状況" Margin="10, 0, 10, 70" HorizontalAlignment="Stretch" VerticalAlignment="Bottom" />
<ProgressBar Height="20" HorizontalAlignment="Stretch" Margin="10, 0, 10, 50" Name="progressBar" VerticalAlignment="Bottom" />
<Button Name="executeBtn" Content="実行(_E)" Margin="10, 0, 0, 10" HorizontalAlignment="Left" VerticalAlignment="Bottom" Height="30" Width="100" />
<Button Name="cancelBtn" Content="キャンセル(_C)" Margin="0, 0, 0, 10" HorizontalAlignment="Center" VerticalAlignment="Bottom" Height="30" Width="100" />
<Button Name="closeBtn" Content="閉じる(_Q)" Margin="0, 0, 10, 10" HorizontalAlignment="Right" VerticalAlignment="Bottom" Height="30" Width="100" />
</Grid>
</Window>
コードビハインド
Imports System.Threading
Class MainWindow
Dim oCancellationTokenSource As CancellationTokenSource = Nothing '個々のキャンセルトークンへのキャンセル通知を管理・送信する
Dim oCancellationToken As CancellationToken = Nothing 'キャンセルをリッスンしているタスクまたはスレッドに渡すトークン
'Progress クラスで使う進捗データ
Class progressClass
Friend progress As Integer = 0
Friend message As String = ""
Sub New(_progress As Integer, _message As String)
progress = _progress
message = _message
End Sub
End Class
'「実行」ボタン
Private Async Sub executeBtn_Click(sender As Object, e As RoutedEventArgs) Handles executeBtn.Click
oCancellationTokenSource = New CancellationTokenSource() '.Cancel されたときの状態をリセットするため(終了時に .Dispose して)New し直す。
oCancellationToken = oCancellationTokenSource.Token
'テスト用のファイル名を 100 個作成
Dim files As New List(Of String)
For i As Integer = 0 To 99
files.Add("File" & i + 1 & ".txt")
Next
'Progress クラスの進捗報告は New Progress されたコンテキストに対して行われるので、非同期処理を開始する前に New しておく。
Dim progress_files = New Progress(Of progressClass)(AddressOf showProgress_files)
Call ボタンを無効化
Try
'別スレッドで非同期処理
Dim bContinue As Boolean = Await Task.Run(Function() 複数ファイルの処理(progress_files, files))
Me.progressMsg.Text = "処理が完了しました"
Catch ex As OperationCanceledException
'ユーザーがキャンセルした
Me.progressMsg.Text = "キャンセルされました"
Catch ex2 As Exception
Me.progressMsg.Text = "何かエラーが起きたみたい"
Finally
oCancellationTokenSource.Dispose
oCancellationTokenSource = Nothing
End Try
Call ボタンを有効化
End Sub
Private Function 複数ファイルの処理(progress_files As IProgress(Of progressClass), files As List(Of String)) As Boolean
'UI スレッドとは別スレッドなのでこの関数内から直接 UI にはアクセスできない(Dispatcher.Invoke を使えば可能)
Dim filesTotal As Integer = files.Count
For i As Integer = 0 To files.Count -1
Dim progressPercent As Integer = CInt((i + 1) / filesTotal * 100) '進捗率
progress_files.Report(New progressClass(progressPercent, files.Item(i) & " (" & (i + 1).ToString("N0") & "/" & filesTotal.ToString("N0") & ")"))
System.Threading.Thread.Sleep(100)
'Call ファイルごとの処理(files.Item(i))
oCancellationToken.ThrowIfCancellationRequested 'ユーザーがキャンセルしたら例外を投げる
'デバッグモードで非同期処理の例外が拾えない?と思ったら --> https://elleneast.com/?p=5918
'例外を使わないやり方
'If token.IsCancellationRequested = True Then
' Return False
'End If
Next
progress_files.Report(New progressClass(100, ""))
Return True
End Function
'進捗表示。この関数は UI スレッドで呼び出される
Private Sub showProgress_files(oProgress As progressClass)
Me.progressMsg.Text = oProgress.message
Call showProgress_setValue(Me.progressBar, oProgress.progress)
End Sub
Private Sub showProgress_setValue(oProgressBar As ProgressBar, progress As Integer)
oProgressBar.Value = progress
'これ↓は Forms の話かな?WPF ではやらなくても大丈夫みたい。
'Vista 以降 ProgressBar の値を変更しても即座にバーに反映されず、少し遅れてバーが伸びてくるようになってしまった。
'これだと実際には処理が完了していても、バーが最後まで伸びてないので処理が未完了に見えることがある。
'設定した値が即座にバーに反映されるようにするため、下記のような変なことをしている。
'https://dobon.net/vb/dotnet/control/pbdisableanimation.html
'If progress < oProgressBar.Maximum Then
' oProgressBar.Value = progress + 1
' oProgressBar.Value = progress
'Else
' oProgressBar.Maximum += 1
' oProgressBar.Value = progress + +1
' oProgressBar.Value = progress
' oProgressBar.Maximum -= 1
'End If
End Sub
Private Sub ボタンを無効化
Me.executeBtn.IsEnabled = False
Me.closeBtn.IsEnabled = False
End Sub
Private Sub ボタンを有効化
Me.executeBtn.IsEnabled = True
Me.closeBtn.IsEnabled = True
End Sub
'「閉じる」ボタン
Private Sub closeBtn_Click(sender As Object, e As RoutedEventArgs) Handles closeBtn.Click
Me.Close
End Sub
'「キャンセル」ボタン
Private Sub cancelBtn_Click(sender As Object, e As RoutedEventArgs) Handles cancelBtn.Click
If oCancellationTokenSource IsNot Nothing Then
oCancellationTokenSource.Cancel 'ユーザーがキャンセルした
End If
End Sub
End Class

