目次
はじめに
大量のデータを処理するマクロを実行していると、「今どのくらい進んでいるのか」が分からず不安になることがあります。この記事では、Excelのステータスバーに進捗率とプログレスバー(視覚的なバー表示)を表示する方法を紹介します。
ステータスバーを使った進捗表示の基本
Excelの画面下部にあるステータスバーには、Application.StatusBarプロパティを使って任意のメッセージを表示できます。処理中に進捗状況を表示すれば、ユーザーに安心感を与えられます。
Application.StatusBar = "処理中です..."
' 何か処理を実行
Application.StatusBar = False ' 元に戻す
Application.StatusBar = Falseとすると、ステータスバーが通常の状態に戻ります。マクロ終了時には必ずこの処理を入れるのがおすすめです。
プログレスバーを実装する
進捗率だけでなく、視覚的なバー表示があるとより分かりやすくなります。ここでは「□」と「■」の文字を組み合わせて、簡易的なプログレスバーを作成します。
【プログレスバー生成の仕組み】
プログレスバーは、進捗率に応じて「■」(塗りつぶし部分)と「□」(未完了部分)の数を計算して文字列を作ります。
Function GetProgressBar(ByVal percent As Long) As String
Dim barLength As Long: barLength = 10 ' バーの長さ
Dim filledCount As Long: filledCount = percent \ 10 ' 塗りつぶす数
GetProgressBar = String(filledCount, "■") _
& String(barLength - filledCount, "□")
End Function
String関数を使うと、指定した文字を指定回数繰り返した文字列を作れます。例えば、進捗率が30%なら、30 \ 10で3となり、「■■■□□□□□□□」という表示になります。
【進捗表示のプロシージャ】
進捗情報をステータスバーに表示する専用のプロシージャを作成すると、メインの処理から呼び出しやすくなります。
Sub ShowProgress(ByVal percent As Long)
Application.StatusBar = "チェック中" _
& "(" & percent & "%)" _
& GetProgressBar(percent)
End Sub
シンプルな構成にすることで、処理速度への影響を最小限に抑えています。
実際の使用例
データチェックなど繰り返し処理を行う際に、この進捗表示を組み込んでみます。ここでは、ダミーデータを自動生成してチェックするサンプルを用意しました。
Const START_ROW As Long = 2 ' データ開始行
Sub CheckData()
Dim lastRow As Long
Dim totalCount As Long
Dim currentCount As Long: currentCount = 0
Dim percent As Long
Dim prevPercent As Long: prevPercent = -1
Dim i As Long
Application.ScreenUpdating = False
' 初期化処理
Call InitializeVariables(lastRow, totalCount, START_ROW)
' 繰り返し処理
For i = START_ROW To lastRow
' メイン処理を呼び出し
Call ExecuteMainProcess(i)
' 進捗更新
currentCount = currentCount + 1
percent = (((currentCount * 100) / totalCount) \ 10) * 10
If percent <> prevPercent Then
Call ShowProgress(percent)
prevPercent = percent
End If
Next i
MsgBox "チェックが完了しました。", vbInformation, "完了"
Application.StatusBar = False
Application.ScreenUpdating = True
End Sub
このコードは、InitializeVariablesで変数を初期化し、ExecuteMainProcessでメイン処理を実行する構造になっています。実際の業務で使う際は、これらのプロシージャの中身を書き換えるだけで対応できます。
【変数初期化プロシージャ】
変数の初期化を専用のプロシージャにまとめることで、データ範囲の取得方法を変更したい場合でも、ここだけを修正すれば済みます。
Sub InitializeVariables(ByRef lastRow As Long, ByRef totalCount As Long, _
ByVal startRow As Long)
' ダミーデータを作成(実際の業務では不要)
Call CreateDummyData
' 最終行を取得
lastRow = Cells(Rows.Count, 1).End(xlUp).Row
' 総件数を計算
totalCount = lastRow - startRow + 1
End Sub
ByRefを使うことで、呼び出し元の変数に値を返せます。実際の業務で使う場合は、CreateDummyDataの行を削除するだけで、既存のデータに対して処理を実行できます。
【メイン処理プロシージャ】
実際のチェック処理を行う部分です。ここでは、デモ用に簡単な処理を入れていますが、実務では書き換えてご使用ください。
Sub ExecuteMainProcess(ByVal rowNumber As Long)
Dim value As Long: value = Cells(rowNumber, 1).Value
If value Mod 2 = 0 Then
Cells(rowNumber, 2).Value = "偶数"
Else
Cells(rowNumber, 2).Value = "奇数"
End If
' 処理時間をシミュレート(実際の業務では不要)
Application.Wait Now + TimeValue("00:00:01")
End Sub
このサンプルでは、A列の値が偶数か奇数かを判定してB列に出力しています。Application.Waitは処理を一時停止する命令で、進捗表示の動きを確認しやすくするために入れています。
【ダミーデータ作成プロシージャ】
デモをすぐに実行できるよう、ダミーデータを自動生成するプロシージャも用意しました。
Sub CreateDummyData()
Const DATA_COUNT As Long = 50 ' 生成するデータ件数
Dim i As Long
' シートをクリア
Cells.Clear
' ヘッダー作成
Cells(1, 1).Value = "データ"
Cells(1, 2).Value = "判定結果"
' ダミーデータ生成
For i = 2 To DATA_COUNT + 1
Cells(i, 1).Value = Int(Rnd() * 100) + 1 ' 1~100の乱数
Next i
End Sub
Rnd()関数は0以上1未満の乱数を生成します。それに100を掛けて整数化することで、1~100の範囲の数値を作成しています。
コード全体の解説
【プロシージャ分割のメリット】
コードを複数のプロシージャに分割することで、以下のようなメリットがあります。
- 保守性の向上 - 修正箇所が明確になり、変更が容易になります
- 再利用性 - 各プロシージャを他の処理でも使い回せます
- テストのしやすさ - 個別のプロシージャ単位で動作確認ができます
特に、CheckDataというメインプロシージャは進捗管理の枠組みだけを提供し、実際の処理内容はExecuteMainProcessに委譲する設計になっているため、業務内容が変わってもCheckData自体を修正する必要がありません。
【進捗率の計算と更新タイミング】
進捗率を計算する部分には工夫が施されています。
percent = (((currentCount * 100) / totalCount) \ 10) * 10
If percent <> prevPercent Then
Call ShowProgress(percent)
prevPercent = percent
End If
まず、(((currentCount * 100) / totalCount) \ 10) * 10という計算で、パーセンテージを10%単位に切り捨てています。例えば、進捗が34%でも39%でも、30%として扱われます。
そして、prevPercentという変数で前回の進捗率を記憶しておき、値が変わったときだけステータスバーを更新する仕組みになっています。これにより、無駄な更新処理を省いて、処理速度を向上させています。
【ByRefとByValの使い分け】
プロシージャ間で値を受け渡す際、ByRefとByValを使い分けることが重要です。
-
ByRef- 参照渡し。呼び出し元の変数自体を渡すため、値を変更できます -
ByVal- 値渡し。値のコピーを渡すため、呼び出し元の変数は変わりません
InitializeVariablesでは、計算結果を呼び出し元に返したいのでByRefを使い、ExecuteMainProcessでは行番号を受け取るだけなのでByValを使っています。
【ScreenUpdatingの制御】
Application.ScreenUpdating = Falseを最初に設定することで、画面の更新を停止して処理速度を向上させています。ただし、ステータスバーは画面更新が停止していても表示されるため、進捗表示には影響しません。
処理が完了したら、必ずApplication.ScreenUpdating = Trueで画面更新を再開することを忘れないようにしてください。
【完了時の通知】
処理が完了したら、MsgBoxでメッセージを表示してユーザーに通知しています。ステータスバーだけでは気づかない場合もあるため、明示的な通知があると親切です。
MsgBox "チェックが完了しました。", vbInformation, "完了"
vbInformationを指定することで、情報アイコン付きのメッセージボックスが表示されます。
動作を確認する際の注意点
【すぐに試せる構成】
このサンプルコードは、そのままコピーして実行するだけで動作します。CreateDummyDataが自動的にA列にデータを生成し、ExecuteMainProcessがそれを処理してB列に結果を出力します。
実際の業務で使う場合は、以下の2箇所を修正するだけです。
-
InitializeVariablesからCreateDummyDataの呼び出しを削除 -
ExecuteMainProcessの中身を実際のチェック処理に書き換える
【処理速度の調整】
サンプルコードでは、進捗表示を確認しやすくするためにApplication.Waitで1秒の待機時間を入れています。
Application.Wait Now + TimeValue("00:00:01")
実際の業務では、この行は不要なので削除してください。また、DATA_COUNTの値を変更することで、生成されるダミーデータの件数を調整できます。件数を増やすと、より長時間の動作を確認できます。
【10%単位の更新について】
進捗率を10%単位で更新する設計になっているため、データ件数が少ない場合、進捗表示が飛び飛びに見えることがあります。例えば、データが5件しかない場合、0%→20%→40%→60%→80%→100%のように表示されます。
より細かい単位で表示したい場合は、計算式を調整してください。
' 5%単位にする場合
percent = (((currentCount * 100) / totalCount) \ 5) * 5
ただし、更新頻度を上げすぎると処理速度に影響が出る可能性があるため、バランスを考えて調整することが大切です。
定数の活用
START_ROWを定数として宣言することで、データの開始行が変わった場合でも、1箇所を修正するだけで対応できます。このように、変更される可能性のある値は定数化しておくと便利です。
まとめ
ステータスバーに進捗率とプログレスバーを表示することで、長時間かかる処理でもユーザーに安心感を与えられます。プロシージャを適切に分割し、進捗更新のタイミングを工夫することで、保守性が高く実用的なコードになります。