VBAでマクロを部品(パーツ)化して使い回す
VBAのコードを沢山書いていくと、「前にもこの処理書いたな、、、」ということがあると思います。
例えば、シートの最終行を取得するとか、画面更新をOFFにするとか、そういうコードです。
前にも似たようなものを書いていた場合に、その過去のコードからコピーして現在作成中のコードにペーストするのも、そこそこ大変ですよね。
何より、どのマクロ有効ブックにそのコードを書いていたかを思い出すのも一苦労だと思います。
そういう場合は、プロシージャ自体をそもそも細分化(パーツ化、部品化)しておき再利用しましょう。
パーツ化(部品化)
パーツ化(部品化)とは、大きなプログラムを小さな部分(部品、モジュール、関数)に分けることを指します。これには以下のような利点があります。
1.再利用性
一度作った部品(関数やモジュール)は、他の部分でも再利用できます。同じ処理を何度も書く必要がなくなります。
2.可読性の向上
大きなプログラムを理解するのは難しいですが、部分に分けると理解しやすくなります。
3.デバッグやテストが簡単
各部品ごとにテストできるので、不具合が見つけやすく修正もしやすいです。これがテスタビリティの向上につながります。
4.保守性の向上
変更が必要になったときも、影響範囲が小さいので修正がラクになります。
では、パーツ化(部品化)とは、どういうことか具体的にコードを交えて説明します。
画面更新や自動計算を停止・再開するマクロ
どんなプロシージャも、高速化のために画面更新や自動計算は停止することが多いと思います。例えば、下記のように。
Sub sbプロシージャ例()
'初期処理
Application.ScreenUpdating = False '画面更新の停止
Application.Calculation = xlCalculationManual '自動計算の停止(手動計算)
Application.DisplayAlerts = False '画面警告の表示の停止
Application.EnableEvents = False 'イベント機能の停止
'メイン処理
'終了処理
Application.ScreenUpdating = True '画面更新の開始
Application.Calculation = xlCalculationAutomatic '自動計算開始
Application.DisplayAlerts = True '画面警告の表示の開始
Application.EnableEvents = True 'イベント機能の開始
End Sub
『初期処理』や『終了処理』等のコメントを記載しないとしても、行数が8行もありコードが長くなる原因になります。
そのため、画面更新の停止や開始等をまとめて設定する下記のようなプロシージャをマクロ有効ブックに保存しておきます。
この汎用プロシージャを利用すれば、メインプロシージャの方で記載する際の行数は8行から2行に減らすことができます。
Public Sub sbSetting(flg As Boolean)
If flg Then
Application.ScreenUpdating = False '画面更新の停止
Application.Calculation = xlCalculationManual '自動計算の停止(手動計算)
Application.DisplayAlerts = False '画面警告の表示の停止
Application.EnableEvents = False 'イベント機能の停止
Else
Application.ScreenUpdating = True '画面更新の開始
Application.Calculation = xlCalculationAutomatic '自動計算開始
Application.DisplayAlerts = True '画面警告の表示の開始
Application.EnableEvents = True 'イベント機能の開始
End If
End Sub
Sub sbSettingの使用例()
Call sbSetting(True) '画面更新OFF等の初期設定ON
'メイン処理
Call sbSetting(False) '画面更新OFF等の初期設定OFF
End Sub
赤枠背景透明図形を挿入するマクロ
もう一つの例を紹介します。
Excelで業務マニュアルを作成する際に、PCの操作画面等をスクリーンショットして、押下するボタン等を赤枠等の塗りつぶしなしの図形で囲むことが私はよくあります。
下記の画像でいうと、「設定」を囲んでいるような図形です。
標準の図形にはそういう設定はないので、自分好みの設定にするのが枠の太さや透明度等の設定が手間です。
Excelの標準機能で、「既定の図形に設定」という機能がありますが、設定したブックのみで有効なので、複数のブックで同じ設定の図形を使用したいという設定には向きません。
そのため、私は下記のようなVBAコードを作成しました。
Public Sub sb赤枠四角図形挿入()
With ActiveCell
ActiveSheet.Shapes.AddShape _
(Type:=msoShapeRectangle, _
Left:=.Left, Top:=.Top, Width:=100, Height:=50).Select
End With
Selection.ShapeRange.Fill.Visible = msoFalse
With Selection.ShapeRange.line
.Visible = msoTrue
.ForeColor.RGB = RGB(255, 0, 0)
.Weight = 4 '太さ
.Transparency = 0.3 '透明度
End With
End Sub
これを実行すると、アクティブセルの位置に、所定サイズの赤枠の四角形な図形(背景透明)が挿入されます。
ここで、四角形だけではなく、楕円形の同様の設定(赤枠背景透明)も作成したくなったとします。
四角形を指すのは、「msoShapeRectangle」なので、同じく図形の形状を示すMsoAutoShapeTypeの一つである「msoShapeOval」に変更すれば、赤枠の楕円形図形をアクティブセルの位置に挿入できます。
Public Sub sb赤枠楕円形図形挿入()
With ActiveCell
ActiveSheet.Shapes.AddShape _
(Type:=msoShapeOval, _
Left:=.Left, Top:=.Top, Width:=100, Height:=50).Select
End With
Selection.ShapeRange.Fill.Visible = msoFalse
With Selection.ShapeRange.line
.Visible = msoTrue
.ForeColor.RGB = RGB(255, 0, 0)
.Weight = 4 '太さ
.Transparency = 0.3 '透明度
End With
End Sub
似たようなコードで作成できました。
上記のSubプロシージャは、「msoShapeRectangle」を「msoShapeOval」に変更しただけで大部分のコードは共通しています。
このようにコードの大部分が共通である場合には、変更して使用したい箇所を引数として可変できるようにして、赤枠図形の挿入という汎用プロシージャを作成します。
Sub sb赤枠四角図形挿入()
Call sb赤枠透明図形挿入(msoShapeRectangle) '図形形状:角が丸い四角
End Sub
Sub sb赤枠楕円図形挿入()
Call sb赤枠透明図形挿入(msoShapeOval) '図形形状:楕円
End Sub
Private Sub sb赤枠透明図形挿入(図形形状 As MsoAutoShapeType)
'MsoAutoShapeTypeはオートシェイプの形状を示す定数
'マニュアル作成作業でよく使用する赤枠・背景透明の図形を挿入する(図形の種類は引数で指定)
With ActiveCell
ActiveSheet.Shapes.AddShape _
(Type:=図形形状, _
Left:=.Left, Top:=.Top, Width:=100, Height:=50).Select
End With
Selection.ShapeRange.Fill.Visible = msoFalse
With Selection.ShapeRange.line
.Visible = msoTrue
.ForeColor.RGB = RGB(255, 0, 0)
.Weight = 4 '太さ
.Transparency = 0.3 '透明度
End With
End Sub
今後、同じ赤枠の塗りつぶしなしの図形で、形状が異なるものは、sb赤枠透明図形挿入Subプロシージャを利用すれば、コードの作成が簡単にできます。
また、部品化することで、コードの行数自体がかなり圧縮されました。
部品として作成したSubプロシージャやFunctionプロシージャは、一度動作確認済みなので、呼び出しして使う際には、引数設定した内容が正しいか等やメインプロシージャの他の部分の確定に注力すればよいので、デバッグにかける時間も削減できます。
特に、よく使用するコードを汎用コードとしておくと本当に便利です。
例えば、対象シートの最終行数を取得するFunction等です。
簡易的に対象シートの最終行数を取得するなら1行コードでもよいですが、フィルターがかかっているとき等でも正しく最終行数を取得するコードは、1行ではすみません。そのコードを毎回、メインプロシージャの中でいちから書くのは大変なので、対象シート等を引数として渡して計算するとよいでしょう。
もしよろしければサポートをお願いします。今後の執筆のかてにします。