Windowsアプリケーションで時間のかかる処理を行う場合、その処理実行中に何もユーザーに応答がないと、それが本当に実行中なのか、もしくはアプリケーションがハングアップ(フリーズ)してしまったのか、区別が付かないことが多い。
これが5秒程度の短い処理ならば、「TIPS:待機状態のマウス・カーソルを表示するには?」で紹介した「待機カーソル」を表示して、処理実行中であることをユーザーに明示すれば問題ないだろう。しかしそれ以上の長い処理では、いつまで処理が続くのか、いつまで待てばよいのかを明示しないと、「待機カーソル」のままハングアップしてしまったのではと不安になるかもしれない。
このような状況を回避する1つの方法は、進行状況(処理の何%が完了したのか)を表すメーター(ProgressBarコントロール)を使った「進行状況ダイアログ」を表示することだ。そこで本稿では、この進行状況ダイアログの実装方法について紹介する。
次の画面は、本稿で準備した進行状況ダイアログである。
本稿では、この進行状況ダイアログを、WindowsフォームであるFormクラスを継承した「WaitDialogクラス」として実装した。WaitDialogクラスのソース・コードは以下からダウンロードできる。
WaitDialogクラスでは、継承元のFormクラス(System.Windows.Forms名前空間)のメンバに加えて、さらに次の表にあるメンバを追加している。
【パブリック・プロパティ】 | ||
IsAborting | 処理がキャンセル(中止)されているかどうか(上掲画面の(5)または(6)が押されたかどうか)を示す値を取得する。キャンセルされた場合はtrue。それ以外はfalse [VB.NET]Public ReadOnly Property IsAborting() As Boolean [C#]public bool IsAborting {get;} |
|
MainMsg | メイン・メッセージ(上掲画面の(1))のテキストを設定する。 [VB.NET]Public WriteOnly Property MainMsg () As String [C#]public string MainMsg {set;} |
|
SubMsg | サブ・メッセージ(上掲画面の(2))のテキストを設定する [VB.NET]Public WriteOnly Property SubMsg () As String [C#]public string SubMsg {set;} |
|
ProgressMsg | 進行状況メッセージ(上掲画面の(3))のテキストを設定する [VB.NET]Public WriteOnly Property ProgressMsg () As String [C#]public string ProgressMsg {set;} |
|
ProgressValue | 進行状況メーターの現在位置(上掲画面の(4)の現在位置)を設定する。既定値は「0」 [VB.NET]Public WriteOnly Property ProgressValue () As Integer [C#]public int ProgressValue {set;} |
|
ProgressMax | 進行状況メーターの範囲の最大値(上掲画面の(4)の右端の値)を設定する。既定値は「100」 [VB.NET]Public WriteOnly Property ProgressMax () As Integer [C#]public int ProgressMax {set;} |
|
ProgressMin | 進行状況メーターの範囲の最小値(上掲画面の(4)の左端の値)を設定する。既定値は「0」 [VB.NET]Public WriteOnly Property ProgressMin () As Integer [C#]public int ProgressMin {set;} |
|
ProgressStep | PerformStepメソッドを呼び出したときに、進行状況メーターの現在位置を進める量を設定する。既定値は「10」 [VB.NET]Public WriteOnly Property ProgressStep () As Integer [C#]public int ProgressStep {set;} |
|
【パブリック・メソッド】 | ||
PerformStep | 進行状況メーターの現在位置をProgressStepプロパティの量だけ進める [VB.NET]Public Sub PerformStep() [C#]public void PerformStep(); |
|
WaitDialogクラスで独自に追加したメンバ |
このWaitDialogクラスでは、進行状況ダイアログを表示するのにShowメソッドを使い、表示したダイアログはCloseメソッドで閉じるという仕様になっている。逆に、FormクラスのShowDialogメソッドによるダイアログの表示を禁止しているので注意してほしい。
このような仕様になっているのは、進行状況ダイアログをモードレス・モードで使用するためである。なお、モードレス・ダイアログについては、「TIPS:モーダル・ダイアログやモードレス・ダイアログを表示するには?」で詳説しているので、詳しくはそちらを参照してほしい。
それでは、この進行状況ダイアログ(WaitDialogクラス)を使用するサンプル・コードを以下に示す。
private void button1_Click(object sender, System.EventArgs e)
{
// 進行状況ダイアログの初期化処理
WaitDialog waitDlg = new WaitDialog();
waitDlg.Owner = this; // ダイアログのオーナーを設定
waitDlg.MainMsg = "進行状況を表示しています……"; // 処理の概要
waitDlg.ProgressMax = 200; // 全体の処理件数
waitDlg.ProgressMin = 0; // 処理件数の最小値(0件から開始)
waitDlg.ProgressStep = 1; // 何件ごとにメーターを進めるか
waitDlg.ProgressValue = 0; // 最初の件数
// オーナーのフォームを無効にする
this.Enabled = false;
// 進行状況ダイアログを表示する
waitDlg.Show();
// 時間のかかる処理
int iCount = 0; // 何件目の処理かを示すカウンタ
for (int i = 0; i < 200; i++)
{
// 処理中止かどうかをチェック
if (waitDlg.IsAborting == true)
{
break;
}
// 進行状況ダイアログの詳細な処理内容を設定
// (0件目、51件目、101件目、151件目で表示更新)
if ((i == 0) ||
((i != 1) && (i % 50 == 1)))
{
waitDlg.SubMsg = "50件ごと表示を更新しています……:" +
((int)(i / 50) + 1).ToString();
}
// 進行状況ダイアログのメーターを設定
waitDlg.ProgressMsg =
((int)(iCount * 100 / 200)).ToString() + "% " +
"(" + iCount.ToString() + " / 200 件)";
// メッセージ処理を促して表示を更新する
Application.DoEvents();
// 何らかの処理
DoSomeWork();
// 処理カウントを1ステップ進める
iCount++;
waitDlg.PerformStep();
}
// 最終メッセージを表示して、閉じるのを少し遅らせる
if (waitDlg.DialogResult == DialogResult.Abort)
{
waitDlg.SubMsg = "処理を中断しました。";
}
else
{
waitDlg.SubMsg = "処理を完了しました。";
waitDlg.ProgressMsg ="100% (200 / 200 件)";
}
Application.DoEvents();
Thread.Sleep(100);
// いったんオーナーをアクティブにする
this.Activate();
// 進行状況ダイアログを閉じる
waitDlg.Close();
// オーナーのフォームを有効に戻す
this.Enabled = true;
}
Private Sub button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles button1.Click
' 進行状況ダイアログの初期化処理
Dim waitDlg As WaitDialog = New WaitDialog
waitDlg.Owner = Me ' ダイアログのオーナーを設定
waitDlg.MainMsg = "進行状況を表示しています……" ' 処理の概要
waitDlg.ProgressMax = 200 ' 全体の処理件数
waitDlg.ProgressMin = 0 ' 処理件数の最小値(0件から開始)
waitDlg.ProgressStep = 1 ' 何件ごとにメーターを進めるか
waitDlg.ProgressValue = 0 ' 最初の件数
' オーナーのフォームを無効にする
Me.Enabled = False
' 進行状況ダイアログを表示する
waitDlg.Show()
' 時間のかかる処理
Dim iCount As Integer = 0 ' 何件目の処理かを示すカウンタ
Dim i As Integer
For i = 0 To 200 - 1 Step i + 1
' 処理中止かどうかをチェック
If waitDlg.IsAborting = True Then
Exit For
End If
' 進行状況ダイアログの詳細な処理内容を設定
' (0件目、51件目、101件目、151件目で表示更新)
If (i = 0) Or ((i <> 1) And (i Mod 50 = 1)) Then
waitDlg.SubMsg = "50件ごと表示を更新しています……:" + _
(CType((i / 50) + 1, Integer)).ToString()
End If
' 進行状況ダイアログのメーターを設定
waitDlg.ProgressMsg = _
(CType((iCount * 100 / 200), Integer)).ToString() + "% " _
+ "(" + iCount.ToString() + " / 200 件)"
' メッセージ処理を促して表示を更新する
Application.DoEvents()
' 何らかの処理
DoSomeWork()
' 処理カウントを1ステップ進める
iCount = iCount + 1
waitDlg.PerformStep()
Next
' 最終メッセージを表示して、閉じるのを少し遅らせる
If waitDlg.DialogResult = DialogResult.Abort Then
waitDlg.SubMsg = "処理を中断しました。"
Else
waitDlg.SubMsg = "処理を完了しました。"
waitDlg.ProgressMsg = "100% (200 / 200 件)"
End If
Application.DoEvents()
Thread.Sleep(100)
' いったんオーナーをアクティブにする
Me.Activate()
' 進行状況ダイアログを閉じる
waitDlg.Close()
' オーナーのフォームを有効に戻す
Me.Enabled = True
End Sub
上記のコードの概要を簡単に説明しておく(コードの詳細な解説は割愛させていただく)。
まず、WaitDialogクラスのインスタンスを生成して、ダイアログのオーナー(所有者)となる自分自身(メインのフォーム)をOwnerプロパティに設定している。さらに、進行状況ダイアログを初期化するため、前述したMainMsgプロパティ、ProgressMaxプロパティ、ProgressMinプロパティ、ProgressStepプロパティ、ProgressValueプロパティを設定している。
次に、フォームのEnabledプロパティをfalseに設定してフォーム全体を無効化したうえで、WaitDialogオブジェクトのShowメソッドを呼び出し、進行状況ダイアログを表示している。
その後、(ダイアログが表示されたままの状態で)「時間のかかる処理」を実行している。処理の実行状況をダイアログ上に表示するために、前述したSubMsgプロパティ、ProgressMsgプロパティにメッセージ・テキストを設定することで進行状況メッセージを更新し、PerformStepメソッドを呼び出すことで進行状況メーターを進めている。
なお、「時間のかかる処理」の中にあるApplication.DoEventsメソッドの呼び出しは、ダイアログの表示内容を描画更新するためのものである。詳しくは、「TIPS:時間がかかる処理での「応答なし」を回避するには?」を参照していただきたい。また、DoSomeWorkメソッドは、「時間のかかる処理」を行うメソッドで、このサンプル・プログラムでは単にスリープしているだけである。
「時間のかかる処理」が終了すると、進行状況に関する最終的なメッセージや進行状況メーター(「100%で完了」、もしくは「50%で中断」など)を表示更新している。その際、Threadクラス(System.Threading名前空間)のSleepメソッドにより、進行状況ダイアログのクローズを少し(100ミリ秒間)遅らせている。
最後に、フォームのActivateメソッドを呼び出してアクティブ化したうえで、WaitDialogオブジェクトのCloseメソッドを呼び出して進行状況ダイアログを閉じ、Enabledプロパティをtrueに再設定することにより、無効化しておいたフォームを再び有効に戻している。
カテゴリ:Windowsフォーム 処理対象:ダイアログ・ボックス
使用ライブラリ:Formクラス(System.Windows.Forms名前空間)
使用ライブラリ:Threadクラス(System.Threading名前空間)
関連TIPS:待機状態のマウス・カーソルを表示するには?
関連TIPS:時間がかかる処理での「応答なし」を回避するには?
Copyright© Digital Advantage Corp. All Rights Reserved.