進行状況ダイアログを表示する方法は「進行状況ダイアログを表示する」でも説明していますが、ここでは、.NET Framework 2.0から追加されたBackgroundWorkerクラスを使って進行状況ダイアログを作成する方法を紹介します。
ここでは進行状況ダイアログのサンプルコードとその使い方だけを示し、BackgroundWorkerクラスについては説明しません。BackgroundWorkerクラスについて詳しくは、「時間のかかる処理の進行状況を表示する」をご覧ください。
では早速作成を開始します。まずはプロジェクトに新しいフォームを追加します(このフォームが進行状況ダイアログになります)。メニューの「プロジェクト」-「Windowsフォームの追加」で追加できます。ここではフォームの名前を「ProgressDialog」(ファイル名は「ProgressDialog.vb」または「ProgressDialog.cs」)とします。
次に、フォームデザイナを使って、フォームに必要なコントロールを配置します。ここでは、プログレスバー、ボタン、ラベル、そしてBackgroundWorkerを1つずつ配置してください。配置の仕方はお任せします。ただし、それぞれのコントロールの名前(Nameプロパティ)は、以下のように変更してください。
コントロール | 名前(Nameプロパティ) |
---|---|
ProgressBar | progressBar1 |
Label | messageLabel |
Button | cancelAsyncButton |
BackgroundWorker | backgroundWorker1 |
フォームのデザインは以上で終了です。残りはコードです。ProgressDialogクラスのコードを、以下のように書きます。
Imports System.ComponentModel Imports System.Windows.Forms ''' <summary> ''' バックグラウンド処理の進行状況を表示するフォーム ''' </summary> Partial Public Class ProgressDialog Inherits Form ''' <summary> ''' ProgressDialogクラスのコンストラクタ ''' </summary> ''' <param name="caption">タイトルバーに表示するテキスト</param> ''' <param name="doWorkHandler">バックグラウンドで実行するメソッド</param> ''' <param name="argument">doWorkで取得できるパラメータ</param> Public Sub New(ByVal caption As String, _ ByVal doWork As DoWorkEventHandler, _ ByVal argument As Object) InitializeComponent() '初期設定 Me.workerArgument = argument Me.Text = caption Me.FormBorderStyle = FormBorderStyle.FixedDialog Me.ShowInTaskbar = False Me.StartPosition = FormStartPosition.CenterParent Me.ControlBox = False Me.CancelButton = Me.cancelAsyncButton Me.messageLabel.Text = "" Me.progressBar1.Minimum = 0 Me.progressBar1.Maximum = 100 Me.progressBar1.Value = 0 Me.cancelAsyncButton.Text = "キャンセル" Me.cancelAsyncButton.Enabled = True Me.backgroundWorker1.WorkerReportsProgress = True Me.backgroundWorker1.WorkerSupportsCancellation = True 'イベント AddHandler Me.Shown, New EventHandler(AddressOf ProgressDialog_Shown) AddHandler Me.cancelAsyncButton.Click, _ New EventHandler(AddressOf cancelAsyncButton_Click) AddHandler Me.backgroundWorker1.DoWork, doWork AddHandler Me.backgroundWorker1.ProgressChanged, _ New ProgressChangedEventHandler( _ AddressOf backgroundWorker1_ProgressChanged) AddHandler Me.backgroundWorker1.RunWorkerCompleted, _ New RunWorkerCompletedEventHandler( _ AddressOf backgroundWorker1_RunWorkerCompleted) End Sub ''' <summary> ''' ProgressDialogクラスのコンストラクタ ''' </summary> Public Sub New(ByVal formTitle As String, _ ByVal doWorkHandler As DoWorkEventHandler) Me.New(formTitle, doWorkHandler, Nothing) End Sub Private workerArgument As Object = Nothing Private _result As Object = Nothing ''' <summary> ''' DoWorkイベントハンドラで設定された結果 ''' </summary> Public ReadOnly Property Result() As Object Get Return Me._result End Get End Property Private _error As Exception = Nothing ''' <summary> ''' バックグラウンド処理中に発生したエラー ''' </summary> Public ReadOnly Property [Error]() As Exception Get Return Me._error End Get End Property ''' <summary> ''' 進行状況ダイアログで使用しているBackgroundWorkerクラス ''' </summary> Public ReadOnly Property BackgroundWorker() As BackgroundWorker Get Return Me.backgroundWorker1 End Get End Property 'フォームが表示されたときにバックグラウンド処理を開始 Private Sub ProgressDialog_Shown(ByVal sender As Object, _ ByVal e As EventArgs) Me.backgroundWorker1.RunWorkerAsync(Me.workerArgument) End Sub 'キャンセルボタンが押されたとき Private Sub cancelAsyncButton_Click(ByVal sender As Object, _ ByVal e As EventArgs) cancelAsyncButton.Enabled = False backgroundWorker1.CancelAsync() End Sub 'ReportProgressメソッドが呼び出されたとき Private Sub backgroundWorker1_ProgressChanged(ByVal sender As Object, _ ByVal e As ProgressChangedEventArgs) 'プログレスバーの値を変更する If e.ProgressPercentage < Me.progressBar1.Minimum Then Me.progressBar1.Value = Me.progressBar1.Minimum ElseIf Me.progressBar1.Maximum < e.ProgressPercentage Then Me.progressBar1.Value = Me.progressBar1.Maximum Else Me.progressBar1.Value = e.ProgressPercentage End If 'メッセージのテキストを変更する Me.messageLabel.Text = DirectCast(e.UserState, String) End Sub 'バックグラウンド処理が終了したとき Private Sub backgroundWorker1_RunWorkerCompleted(ByVal sender As Object, _ ByVal e As RunWorkerCompletedEventArgs) If e.Error IsNot Nothing Then MessageBox.Show(Me, "エラー", _ "エラーが発生しました。" & vbCrLf & vbCrLf & _ e.Error.Message, MessageBoxButtons.OK, _ MessageBoxIcon.Error) Me._error = e.Error Me.DialogResult = DialogResult.Abort ElseIf e.Cancelled Then Me.DialogResult = DialogResult.Cancel Else Me._result = e.Result Me.DialogResult = DialogResult.OK End If Me.Close() End Sub End Class
using System; using System.ComponentModel; using System.Windows.Forms; /// <summary> /// バックグラウンド処理の進行状況を表示するフォーム /// </summary> public partial class ProgressDialog : Form { /// <summary> /// ProgressDialogクラスのコンストラクタ /// </summary> /// <param name="caption">タイトルバーに表示するテキスト</param> /// <param name="doWorkHandler">バックグラウンドで実行するメソッド</param> /// <param name="argument">doWorkで取得できるパラメータ</param> public ProgressDialog(string caption, DoWorkEventHandler doWork, object argument) { InitializeComponent(); //初期設定 this.workerArgument = argument; this.Text = caption; this.FormBorderStyle = FormBorderStyle.FixedDialog; this.ShowInTaskbar = false; this.StartPosition = FormStartPosition.CenterParent; this.ControlBox = false; this.CancelButton = this.cancelAsyncButton; this.messageLabel.Text = ""; this.progressBar1.Minimum = 0; this.progressBar1.Maximum = 100; this.progressBar1.Value = 0; this.cancelAsyncButton.Text = "キャンセル"; this.cancelAsyncButton.Enabled = true; this.backgroundWorker1.WorkerReportsProgress = true; this.backgroundWorker1.WorkerSupportsCancellation = true; //イベント this.Shown += new EventHandler(ProgressDialog_Shown); this.cancelAsyncButton.Click += new EventHandler(cancelAsyncButton_Click); this.backgroundWorker1.DoWork += doWork; this.backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged); this.backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted); } /// <summary> /// ProgressDialogクラスのコンストラクタ /// </summary> public ProgressDialog(string formTitle, DoWorkEventHandler doWorkHandler) : this(formTitle, doWorkHandler, null) { } private object workerArgument = null; private object _result = null; /// <summary> /// DoWorkイベントハンドラで設定された結果 /// </summary> public object Result { get { return this._result; } } private Exception _error = null; /// <summary> /// バックグラウンド処理中に発生したエラー /// </summary> public Exception Error { get { return this._error; } } /// <summary> /// 進行状況ダイアログで使用しているBackgroundWorkerクラス /// </summary> public BackgroundWorker BackgroundWorker { get { return this.backgroundWorker1; } } //フォームが表示されたときにバックグラウンド処理を開始 private void ProgressDialog_Shown(object sender, EventArgs e) { this.backgroundWorker1.RunWorkerAsync(this.workerArgument); } //キャンセルボタンが押されたとき private void cancelAsyncButton_Click(object sender, EventArgs e) { cancelAsyncButton.Enabled = false; backgroundWorker1.CancelAsync(); } //ReportProgressメソッドが呼び出されたとき private void backgroundWorker1_ProgressChanged( object sender, ProgressChangedEventArgs e) { //プログレスバーの値を変更する if (e.ProgressPercentage < this.progressBar1.Minimum) { this.progressBar1.Value = this.progressBar1.Minimum; } else if (this.progressBar1.Maximum < e.ProgressPercentage) { this.progressBar1.Value = this.progressBar1.Maximum; } else { this.progressBar1.Value = e.ProgressPercentage; } //メッセージのテキストを変更する this.messageLabel.Text = (string)e.UserState; } //バックグラウンド処理が終了したとき private void backgroundWorker1_RunWorkerCompleted( object sender, RunWorkerCompletedEventArgs e) { if (e.Error != null) { MessageBox.Show(this, "エラー", "エラーが発生しました。\n\n" + e.Error.Message, MessageBoxButtons.OK, MessageBoxIcon.Error); this._error = e.Error; this.DialogResult = DialogResult.Abort; } else if (e.Cancelled) { this.DialogResult = DialogResult.Cancel; } else { this._result = e.Result; this.DialogResult = DialogResult.OK; } this.Close(); } }
これで出来上がりです。
このProgressDialogクラスを実際に使用したコードを以下に示します。なおこのコードは進行状況ダイアログを呼び出すフォームのクラスに書かれており、そのフォームには Button1 というボタンコントロールが配置されているものとします。Button1 をクリックすると進行状況ダイアログが表示され、100ミリ秒ごとにプログレスバーが伸びていき、100%になると自動的に閉じられます。
'Button1のClickイベントハンドラ Private Sub Button1_Click(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles Button1.Click 'ProgressDialogオブジェクトを作成する Dim pd As New ProgressDialog("進行状況ダイアログのテスト", _ New DoWorkEventHandler(AddressOf ProgressDialog_DoWork), _ 100) '進行状況ダイアログを表示する Dim result As DialogResult = pd.ShowDialog(Me) '結果を取得する If result = DialogResult.Cancel Then MessageBox.Show("キャンセルされました") ElseIf result = DialogResult.Abort Then 'エラー情報を取得する Dim ex As Exception = pd.Error MessageBox.Show("エラー: " + ex.Message) ElseIf result = DialogResult.OK Then '結果を取得する Dim stopTime As Integer = CInt(pd.Result) MessageBox.Show("成功しました: " & stopTime.ToString()) End If '後始末 pd.Dispose() End Sub 'DoWorkイベントハンドラ Private Sub ProgressDialog_DoWork(ByVal sender As Object, _ ByVal e As DoWorkEventArgs) Dim bw As BackgroundWorker = DirectCast(sender, BackgroundWorker) 'パラメータを取得する Dim stopTime As Integer = CInt(e.Argument) '時間のかかる処理を開始する For i As Integer = 1 To 100 'キャンセルされたか調べる If bw.CancellationPending Then 'キャンセルされたとき e.Cancel = True Return End If '指定された時間待機する System.Threading.Thread.Sleep(stopTime) 'ProgressChangedイベントハンドラを呼び出し、 'コントロールの表示を変更する bw.ReportProgress(i, i.ToString() & "% 終了しました") Next '結果を設定する e.Result = stopTime * 100 End Sub
//Button1のClickイベントハンドラ private void Button1_Click(object sender, System.EventArgs e) { //ProgressDialogオブジェクトを作成する ProgressDialog pd = new ProgressDialog("進行状況ダイアログのテスト", new DoWorkEventHandler(ProgressDialog_DoWork), 100); //進行状況ダイアログを表示する DialogResult result = pd.ShowDialog(this); //結果を取得する if (result == DialogResult.Cancel) { MessageBox.Show("キャンセルされました"); } else if (result == DialogResult.Abort) { //エラー情報を取得する Exception ex = pd.Error; MessageBox.Show("エラー: " + ex.Message); } else if (result == DialogResult.OK) { //結果を取得する int stopTime = (int)pd.Result; MessageBox.Show("成功しました: " + stopTime.ToString()); } //後始末 pd.Dispose(); } //DoWorkイベントハンドラ private void ProgressDialog_DoWork(object sender, DoWorkEventArgs e) { BackgroundWorker bw = (BackgroundWorker)sender; //パラメータを取得する int stopTime = (int)e.Argument; //時間のかかる処理を開始する for (int i = 1; i <= 100; i++) { //キャンセルされたか調べる if (bw.CancellationPending) { //キャンセルされたとき e.Cancel = true; return; } //指定された時間待機する System.Threading.Thread.Sleep(stopTime); //ProgressChangedイベントハンドラを呼び出し、 //コントロールの表示を変更する bw.ReportProgress(i, i.ToString() + "% 終了しました"); } //結果を設定する e.Result = stopTime * 100; }
上記のコードを説明します。
まず、ProgressDialogのコンストラクタのパラメータに、ダイアログのタイトルバーに表示する文字列、BackgroundWorkerのDoWorkイベントハンドラ、さらにDoWorkイベントハンドラで取得できるパラメータを指定しています。ただし、3番目のパラメータは省略できます。
ProgressDialogのShowDialogメソッドを呼び出すと、進行状況ダイアログが表示され、同時にDoWorkイベントハンドラが実行されます。
DoWorkイベントハンドラには、バックグラウンドで行う時間のかかる処理を記述します。詳しくは「進行状況ダイアログを表示する」で説明していますが、最も注意しなければいけないのは、DoWorkイベントハンドラ内では絶対にコントロール(フォームを含む)を操作してはいけないということです。そうしないと、例外System.InvalidOperationExceptionが発生します。
ユーザーが進行状況ダイアログのキャンセルボタンを押したかは、CancellationPendingプロパティで分かります。CancellationPendingプロパティの値を随時調べ、Trueになったらキャンセルの処理を行います。この時、DoWorkEventArgs.CancelをTrueにして、DoWorkイベントハンドラを終了させます。
進行状況ダイアログのプログレスバーの値とメッセージの内容を変更するには、ReportProgressメソッドを呼び出します。ReportProgressメソッドの第1パラメータには、プログレスバーの値を0~100までの整数で指定します。第2パラメータには、メッセージの内容をString型で指定します。
DoWorkイベントハンドラが終了すると、ProgressDialog.ShowDialogメソッドはDialogResultを返します。処理が正常に完了したときは、DialogResult.OKを返します。キャンセルされたときは、DialogResult.Cancelを返します。エラーが発生したときは、DialogResult.Abortを返します。