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