[VBA]進捗状況をユーザーに通知する

やりたいこと

とあるツールが複数の機能を持ち合わせているとき、次の弊害が起こりうる。

問題点

  • 機能ごとに分けられたボタンが複数表示されており、どこからクリックしてよいかわからない。
  • 処理が完了するごとに、「完了しました」のメッセージが表示され、OKボタンを押して閉じるのが手間である。
  • さらに、ユーザーが慣れてきたタイミングで本来はエラーが表示されていたにも関わらず、思考停止でOKを押して、リカバリができなくなってしまう場合がある。
  • メッセージは一度閉じると確認できなくなってしまうため、ユーザーの注意力がメッセージボックスに割かれる。

改善点

  • 処理結果については、常に画面上に表示されるようにする。
  • 次にどのボタンを押せばよいのか明確にする。
  • すでに実行したボタンは押せないようにする。

 

イメージ

 

VBAからいじるのは、「ボタン」「ステータス」「実行結果」の3点であとは固定のレイアウトである。

 

ソースコード

実行ボタン管理

Option Explicit

' ----------------------------------------------------------------------------------------------------------------------------------------
' 定数 定義
' ----------------------------------------------------------------------------------------------------------------------------------------
' シート名
Private Const TARGET_SHEET As String = "Main2"

' 色定義
Private Const ENABLED_EXEC_BUTTON_BACKGROUND_COLOR = "#2E75B6"
Private Const ENABLED_EXEC_BUTTON_FONT_COLOR = "#FFFFFF"
Private Const ENABLED_EXEC_BUTTON_LINE_COLOR_COLOR = "#1F4E79"
Private Const DISABLED_EXEC_BUTTON_BACKGROUND_COLOR = "#BFBFBF"
Private Const DISABLED_EXEC_BUTTON_FONT_COLOR = "#F2F2F2"
Private Const DISABLED_EXEC_BUTTON_LINE_COLOR = "#A6A6A6"

' ----------------------------------------------------------------------------------------------------------------------------------------
' 列挙型 定義
' ----------------------------------------------------------------------------------------------------------------------------------------
Public Enum eExecButtonId
    Initialize = 1
    ImportFile = 2
    PADAutomate = 3
    ExportFile = 4
End Enum

' ----------------------------------------------------------------------------------------------------------------------------------------
' Public メソッド
' ----------------------------------------------------------------------------------------------------------------------------------------
' ボタンを有効にする
Public Sub setActive(execButtonId As eExecButtonId)
    Call setActivate(execButtonId, True)
End Sub

' ボタンを無効にする
Public Sub setInactive(execButtonId As eExecButtonId)
    Call setActivate(execButtonId, False)
End Sub

' ----------------------------------------------------------------------------------------------------------------------------------------
' Private メソッド
' ----------------------------------------------------------------------------------------------------------------------------------------
' ボタンを取得する
Private Sub setActivate(execButtonId As eExecButtonId, isActive As Boolean)
    Dim ws As Worksheet
    Set ws = ThisWorkbook.Worksheets(TARGET_SHEET)
    
    Dim buttonName As String
    buttonName = getButtonName(execButtonId)
    Dim macroName As String
    macroName = getMacroName(execButtonId)
    
    Dim shape As shape
    Dim targetShape As shape
    For Each shape In ws.Shapes
        If (buttonName = shape.Name) Then
            Set targetShape = shape
            Exit For
        End If
    Next
    
    If (targetShape Is Nothing) Then
        Call Err.Raise(1025, "", "ボタン名「」が存在しませんでした。")
    End If
    
    With targetShape
        If (isActive = True) Then
            .TextFrame2.TextRange.Font.Fill.ForeColor.RGB = convertColorToRGB(ENABLED_EXEC_BUTTON_FONT_COLOR)
            .Fill.ForeColor.RGB = convertColorToRGB(ENABLED_EXEC_BUTTON_BACKGROUND_COLOR)
            .Line.ForeColor.RGB = convertColorToRGB(ENABLED_EXEC_BUTTON_LINE_COLOR_COLOR)
            .OnAction = macroName
        Else
            .TextFrame2.TextRange.Font.Fill.ForeColor.RGB = convertColorToRGB(DISABLED_EXEC_BUTTON_FONT_COLOR)
            .Fill.ForeColor.RGB = convertColorToRGB(DISABLED_EXEC_BUTTON_BACKGROUND_COLOR)
            .Line.ForeColor.RGB = convertColorToRGB(DISABLED_EXEC_BUTTON_LINE_COLOR)
            .OnAction = "notifyButtonDisabled"
        End If
    End With
End Sub

' ボタン名を取得する
Private Function getButtonName(execButtonId As eExecButtonId) As String
    Dim processName As String
    Select Case execButtonId
        Case eExecButtonId.Initialize
            processName = "Initialize"
        Case eExecButtonId.ImportFile
            processName = "ImportFile"
        Case eExecButtonId.PADAutomate
            processName = "PADAutomate"
        Case eExecButtonId.ExportFile
            processName = "ExportFile"
    End Select
    getButtonName = processName & "Button"
End Function

' ボタン実行時に起動するマクロ名を取得する
Private Function getMacroName(execButtonId As eExecButtonId) As String
    Dim macroName As String
    Select Case execButtonId
        Case eExecButtonId.Initialize
            macroName = "XX01_Initialize"
        Case eExecButtonId.ImportFile
            macroName = "XX02_ImportFile"
        Case eExecButtonId.PADAutomate
            macroName = "XX03_PADAutomate"
        Case eExecButtonId.ExportFile
            macroName = "XX04_ExportFile"
    End Select
    getMacroName = macroName
End Function

' ボタンが非活性であることを通知する
Private Sub notifyButtonDisabled()
    Call MsgBox("前処理が完了していないため、このボタンは現在押すことができません。")
End Sub

' 文字色をRGB形式に変換する
Private Function convertColorToRGB(color As String) As Long
    Dim r As Long
    Dim g As Long
    Dim b As Long
    r = Val("&H" & Mid(color, 2, 2))
    g = Val("&H" & Mid(color, 4, 2))
    b = Val("&H" & Mid(color, 6, 2))
    convertColorToRGB = RGB(r, g, b)
End Function

 

ステータス表示


Option Explicit

' ----------------------------------------------------------------------------------------------------------------------------------------
' 定数 定義
' ----------------------------------------------------------------------------------------------------------------------------------------
' 座標定義
Private Const TARGET_SHEET As String = "Main2"
Private Const PROGRESS_BEGIN_ROW As Long = 7
Private Const STEP_STATUS_COLUMN As Long = 8
Private Const EXECUTE_MESSAGE_COLUMN As Long = 9

' セマンティックカラー
Private Const COLOR_SUCCESS = "#197A4B"
Private Const COLOR_ERROR = "#CE0000"
Private Const COLOR_WARNING = "#FB5B01"
' ----------------------------------------------------------------------------------------------------------------------------------------
' 列挙型 定義
' ----------------------------------------------------------------------------------------------------------------------------------------
Public Enum eStepId
    Step1 = 1
    Step2 = 2
    Step3 = 3
    Step4 = 4
    Step5 = 5
    Step6 = 6
End Enum

Public Enum eStepStatus
    NotStarted      ' 未実行
    Success           ' 完了
    AnyError         ' エラー
    InProgress      ' 実行中
End Enum

' ----------------------------------------------------------------------------------------------------------------------------------------
' Public メソッド
' ----------------------------------------------------------------------------------------------------------------------------------------
' ステータスを更新する
Public Sub updateStepStatus(stepId As eStepId, stepStatus As eStepStatus, executeMessage As String)
    Dim ws As Worksheet
    Set ws = ThisWorkbook.Worksheets(TARGET_SHEET)

    Dim stepStatusDisplayName As String
    Dim displayColor As Long
        Select Case stepStatus
        Case eStepStatus.NotStarted
            stepStatusDisplayName = "ー"
            displayColor = convertColorToRGB("#A6A6A6")
        Case eStepStatus.Success
            stepStatusDisplayName = "完了"
            displayColor = convertColorToRGB(COLOR_SUCCESS)
        Case eStepStatus.AnyError
            stepStatusDisplayName = "エラー"
            displayColor = convertColorToRGB(COLOR_ERROR)
        Case eStepStatus.InProgress
            stepStatusDisplayName = "実行中"
            displayColor = convertColorToRGB("#F9D57B")
    End Select
    
    Dim stepStatusCell As Range
    Set stepStatusCell = ws.Cells( _
        PROGRESS_BEGIN_ROW + stepId - 1, _
        STEP_STATUS_COLUMN)
    Dim executeMessageCell As Range
    Set executeMessageCell = ws.Cells( _
        PROGRESS_BEGIN_ROW + stepId - 1, _
        EXECUTE_MESSAGE_COLUMN)
    
    stepStatusCell.value = stepStatusDisplayName
    stepStatusCell.Font.color = displayColor
    executeMessageCell.value = executeMessage
    executeMessageCell.Font.color = displayColor
End Sub

' ----------------------------------------------------------------------------------------------------------------------------------------
' Private メソッド
' ----------------------------------------------------------------------------------------------------------------------------------------
' 文字色をRGB形式に変換する
Private Function convertColorToRGB(color As String) As Long
    Dim r As Long
    Dim g As Long
    Dim b As Long
    r = Val("&H" & Mid(color, 2, 2))
    g = Val("&H" & Mid(color, 4, 2))
    b = Val("&H" & Mid(color, 6, 2))
    convertColorToRGB = RGB(r, g, b)
End Function

 

ソースコード着目点

デメリット

ボタン押下時に呼びだすメソッド名については、文字列で指定しているのがちょっと気持ち悪い。

当然VBAでは、リフレクションでgetMethodName(method)みたいなことはできない。

 

メリット

実行ボタンと実行結果の出力を分けることで、実行ボタンの単位 ≠実行結果の出力とすることができる(イメージ図の通り)