VBAで表示するメッセージをシートで管理する

画面に表示するメッセージの管理についてですが、Javaや.NETなどでアプリケーションを作成する場合には、定義を外出しにすることがほとんどだと思います。

しかし、ExcelVBAのアプリケーションの場合は、メッセージ管理されているものが少なく、ほとんどがソースコードの中にハードコーディングされている気がします。

CONSTで定義されているものはマシで、Msgbox関数にメッセージが直書きされているものも多いです。

Excelはシートにデータを持つことができるので、メッセージはシートで管理すべきと考えています。

具体的な方法について記載していきたいと思います。

メッセージをソースで管理するデメリット

メッセージをソースで管理するデメリットは、以下2つがあるかと思います。

  • ソースを変更するので、(現場のルールによっては)テストが必要になる
  • 同じようなメッセージを作成してしまう危険がある

ソースに直書きしてしまうと、メッセージの内容を変更したいときに、ソースを検索して修正をしなくてはいけなくなります。

メッセージの文言とはいえ、ソースを修正するとテストが必要になるケースが多いです。

また、メッセージが一覧で管理されていないと似たメッセージを複数作成してしまう危険があります。

例えば、既に「商品が取得できません。」というメッセージを定義している定数があるのに「商品がありません。」というメッセージの定数を作ってしまうなどです。

その場合、「商品という言葉は製品に変えてくれ」と顧客に言われた場合に修正量がかなり多くなってしまいます。

メッセージが一覧で見れるようになっていれば、上記のような重複を防ぐことが出来ます。

シートにメッセージの一覧を作成する

冒頭に記載した通り、メッセージの一覧を保持しているシートを作成し、そこにメッセージ定義を書いて管理するのが良いかと思います。

具体的には以下の列を作成して管理します。

項目説明
メッセージ管理番号メッセージを一意に特定する番号を設定する。この番号を使用してメッセージを呼び出す
タイトルウィンドウタイトルを設定する。
内容メッセージの本文を設定する。
アイコンの種類アイコンの種類を設定する。
ボタンの種類ボタンの種類を設定する。

アイコンの種類はリストボックスで以下を設定できるようにします。

  • vbCritical
  • vbQuestion
  • vbExclamation
  • vbInformation

ボタンの種類はリストボックスで以下を設定できるようにします。

  • vbOKOnly
  • vbOKCancel
  • vbAbortRetryIgnore
  • vbYesNoCancel
  • vbYesNo
  • vbRetryCancel

ボタンの種類とアイコンは、メッセージ上の以下の箇所のことです。

そして、上記シートに記載した「メッセージ管理番号」を引数で与えればメッセージが表示されるようにVBAでプログラムを作成します。


メッセージの一覧からメッセージを呼び出すVBAプログラム

以下のようなプログラムを作成し、メッセージを呼び出す共通関数として使用します。

↓クラスモジュールで作成(MsgDataクラス)

Option Explicit

Enum COLS
    COL_NO = 1
    COL_TITLE
    COL_CONTEXT
    COL_ICON
    COL_BUTTONS
End Enum

Private title As String
Private context As String
Private buttons As Integer

Public Sub setcontextData(ws As Worksheet, row As Long)
    title = ws.Cells(row, COLS.COL_TITLE).Value
    context = ws.Cells(row, COLS.COL_CONTEXT).Value
    
    Select Case ws.Cells(row, COLS.COL_BUTTONS).Value
    Case "vbOKOnly"
    buttons = vbOKOnly
    Case "vbOKCancel"
    buttons = vbOKCancel
    Case "vbAbortRetryIgnore"
    buttons = vbAbortRetryIgnore
    Case "vbYesNoCancel"
    buttons = vbYesNoCancel
    Case "vbYesNo"
    buttons = vbYesNo
    Case "vbRetryCancel"
    buttons = vbRetryCancel
    End Select
    
    Select Case ws.Cells(row, COLS.COL_ICON).Value
    Case "vbCritical"
    buttons = buttons + vbCritical
    Case "vbQuestion"
    buttons = buttons + vbQuestion
    Case "vbExclamation"
    buttons = buttons + vbExclamation
    Case "vbInformation"
    buttons = buttons + vbInformation
    End Select

End Sub

Public Sub showMsg()
    Call MsgBox(context, buttons, title)
End Sub

↓標準モジュールで作成

Private dicMsg As Object

Private Sub init()
    Const SHEET_NAME = "メッセージ定義"
    Const COL_NO = 1
    
    Dim ws As Worksheet: Set ws = ThisWorkbook.Worksheets(SHEET_NAME)
    Dim row As Long
    Dim clsMsg As msgData
    
    Set dicMsg = CreateObject("Scripting.Dictionary")
    For row = 2 To ws.Cells(Rows.Count, 1).End(xlUp).row
        If Not dicMsg.exists(ws.Cells(row, COL_NO).Value) Then
            Set clsMsg = New msgData
            Call clsMsg.setcontextData(ws, row)
            dicMsg.Add ws.Cells(row, COL_NO).Value, clsMsg
        End If
    Next
End Sub

Public Sub openMsg(id As String)
    If dicMsg Is Nothing Then init
    
    If dicMsg.exists(id) Then
        Dim clsMsg As msgData
        Set clsMsg = dicMsg.Item(id)
        Call clsMsg.showMsg
    Else
        MsgBox "未定義のエラーです"
    End If
End Sub

↓呼び出し方法

Public Sub test()
    Call openMsg("MSG001")
End Sub

↓結果

メッセージ一覧を定義しているワークシート名は「メッセージ定義」というワークシート名になっていることを想定しています。

以下からサンプルをダウンロードできます。

https://vbaexcel.slavesystems.com/product/Msgサンプル.zip

メッセージに引数を指定できるようにする

上記の方法を使用していると不便に感じることが出てくるかもしれません。

それは、一部だけが異なるメッセージを使いまわしたい場合です。

具体例を出すと、以下のメッセージを定義しなければいけないとします。

  • 同じ取引先は登録することができません
  • 同じ部署は登録することができません
  • 同じ社員は登録することができません

上記は「取引先、部署、社員」という文言が違うだけで他はすべて同じです。

仮に、「登録できません。」の部分を変更する必要が発生した場合は修正するメッセージが多くなってしまいます。

そこで、「取引先、部署、社員」の部分を引数で渡すよう実装するのが良いかと思います。

まず、ワークシート上のメッセージ定義を以下のようにします。

同じ#{1}は登録することができません

そして、上記の#{1}の部分をreplaceするようにVBAのコードを書けばよいかと思います。

Public Sub openMsg(id As String, ParamArray arg() As Variant)
    If dicMsg Is Nothing Then init
    
    If dicMsg.exists(id) Then
        Dim clsMsg As msgData
        Set clsMsg = dicMsg.Item(id)
        For i = 0 To UBound(arg)
            clsMsg.context = Replace(clsMsg.context, "#{" & i + 1 & "}", "")
        Next
        Call clsMsg.showMsg
    Else
        MsgBox "未定義のエラーです"
    End If
End Sub

コメントを残す

メールアドレスが公開されることはありません。

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)