8
22

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Excel VBAのインターフェースを活用して、マクロの進捗表示機能を再利用可能にする。

Posted at

#はじめに

Excel VBAでインターフェースを活用して共通処理をまとめ、再利用できるようにしようとした時のメモです。

本記事のコードは長くなるので疑似コードです。
色々省略しています。

#環境
Microsoft Excel 2010

#読む人の前提
Excel VBAでクラスモジュールを使用したことがある人。
オブジェクト指向言語の経験者
 (私はC#を使っていますので、C#やVB.net、JAVAの感覚が近いかと。)

#参考

このサイトは非常に参考になりました。

https://sites.google.com/site/compositiosystemae/home/vbaworld/upper/interface

#やりたかったこと

進捗表示のダイアログを表示して、データを処理するたびにバーが伸びていくマクロをいくつか作っていました。
行毎のデータ、シート毎、ファイル毎とまちまちだったので、インターフェースを使用して共通化しました。

#詳細

##そもそもインターフェースの使い方
VBAでは、クラスの継承はできませんが、インターフェースの実装はできます。
プロシージャの中身をすべて空にすることでインターフェースとなります。
インターフェースで定義されたプロシージャを実装する時は、実装クラス側でImpements [インターフェース名]で宣言し、各プロシージャは[インターフェース名]_[プロシージャ名]で定義します。
呼び出すときは、インターフェース型の変数に格納することで、呼び出しが可能です。

ISample.cls
public sub Test
End sub
A.cls
Implements ISample

public sub ISample_Test
    debug.print "A"
end sub
B.cls
Implements ISample

public sub ISample_Test
    debug.print "B"
end sub
Main.bas
dim aa as New A
dim bb as New B
dim sample as ISample

set sample = aa
sample.Test   '結果:A

set sample = bb
sample.Test   '結果:B

##共通化する前
ProgressControllerというクラスを定義し、startすると、中でループを回し、
CancelFormというフォームのProgressBarの値を1ずつ足しています。(cancelForm.Imcrement)

ProgressController.cls
public sub Start
    CancelForm.show
    for r = 1 to end
        'r行目に対するなんらかの処理
        CancelForm.Increment
        DoEvents
    next
    unload CancelForm
end sub

上記の例は行ごとのデータ用ですが、このクラスが対象データ種ごとにありました。
##インターフェースを使用した共通化
###インターフェースを定義
行毎、シート毎、ファイル毎は気にせず、データを一つずつ列挙するためのIEnumeratorクラス(インターフェース)を定義します。
型名はC#ライクにIをプリフィックスとします。

IEnumerator.cls
public sub Start()
end sub

###インターフェースからイベント発生
IEnumerator.Startで開始して、ひとつずつ列挙する時にCancelForm.Incrementを呼びたいので、イベントを使用します。
インターフェースにイベントを定義できないので、イベントを発生させる別オブジェクト経由にします。

EnumeratorEventObject.cls
Public Event OnAction(sender As IEnumerator)
Public Sub Raise(sender As IEnumerator)
    RaiseEvent OnAction(sender)
End Sub
IEnumerator.cls
Public Property Get EventObject() As EnumeratorEventObject
End Property
public sub Start()
end sub

IEnumeratorの実装側で、1データ処理するたびに、EventObject.Raise(Me)を呼ぶと、イベントが発生します。

###イベントハンドラに処理を移す

ProgressControllerを下記のように変更します。

ProgressController.cls
private withevents eventObject as EnumeratorEventObject

public sub Start(enumerator as IEnumerator)
    set eventObject = enumerator.EventObject
    cancelform.show
    '列挙開始
    enumerator.Start
    '全て列挙完了したらCancelFormを閉じる
    unload cancelForm
end sub

'データごとに呼び出されるイベントハンドラ
private sub eventObject_OnAction(sender as IEnumerator)
    cancelForm.Increment
    DoEvents
end sub

###実装例

ExcelRowEnumerator.cls
Implements IEnumerator

public event Action(sender as ExcelRowEnumerator)
'対象シート
private m_Sheet as WorkSheet
public property set Target(value as WorkSheet)
    set m_Sheet = value
end property
'現在行
private m_CurrentRow as integer
public property get CurrentRow as integer
    CurrentRow = m_CurrentRow
end property


'初期化
Private Sub Class_Initialize()
    Set m_EventObject = New EnumeratorEventObject
End Sub

'列挙開始
public sub Start()
    dim i as integer

    for i = 1 to 100       '実際は、ここはプロパティ化する
        m_CurrentRow = i

        Action Me
        DoEvents
        m_EventObject.Raise Me
    next    
end sub

'以下、IEnumeratorの実装
Private Property Get IEnumerator_EventObject() As EnumeratorEventObject
    Set IEnumerator_EventObject = m_EventObject
End Property
Private Sub IEnumerator_Start()
    Me.Start
End Sub
ToolMain.cls
'実際の行ごとの処理はExcelRowEnumeratorのイベントで処理する
private withevents rowEnumerator1 as ExcelRowEnumerator

public sub Main()
    set rowEnumerator1 = new ExcelRowEnumerator

    dim progressController as ProgressController
    set progressController = New ProgressController

    progressController.Start rowEnumerator1
end sub

private sub rowEnumerator1_Action(sender as ExcelRowEnumerator)
    'ここにアプリケーションとして、行ごとの処理を書く
end sub

例えば標準モジュールからの呼び出し

Module1.bas
public sub Main1
    dim tool as New ToolMain
    tool.Main
end sub

プロシージャの呼び出しと完了を黒、イベントによる通知を赤の矢印で図に表してみました。
ProgressController.png

#まとめ

インターフェースと移譲を利用して、オブジェクト指向の感覚で実装の共通化ができました。

Module1.basとToolMainだけ毎回作成して、後は再利用できます。

8
22
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
22

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?