第 24 章 プログラム内での VISIO UNDO マネージャの使用
次の例は、図面とパラレル モデルとが同期したundo ユニットの作成方法です。この場合、m_nCount と呼ばれる単純な変数が、図面中の図形の数を示しています。この変数は、図面に図形が 1 つ追加されるたびにインクリメントされます。
VBAUndoUnit と呼ばれるクラス モジュールには、続いて ShapeAdded イベントが Undo された場合と Redo された場合に、この変数をインクリメントまたはデクリメントするためのロジックが含まれています。VBAUndoUnit には、元に戻された状態を保持するフラグが含まれています。このオブジェクトは最初は undo スタックにあるため、フラグは FALSE に初期化されます。最初に呼び出された後は、undo ロジックを実行し、undo された状態を TRUE にして、redo スタックに配置されます。
この例では、Visual Basic エディタの [イミディエイト] ウィンドウを使って、プロジェクトでの図面とデータの同期方法を表示します。
コードの最初のブロックは、ThisDocument クラス モジュールの ShapeAdded イベント ハンドラに記述されています。プログラムは、m_nCount を 1 だけインクリメントし、undo ユニットを Visio の Undo マネージャの undo スタックに追加します。Visio エンジンでは、イベント ハンドラが呼び出されるとスコープが自動的に作成されるため、図面に図形を追加した直後に undoすると、undo ユニットが実行されます。
ThisDocument には、変数のインクリメントとデクリメントのためのプロシージャも含まれています。
Private m_nCount As Long
Private Sub Document_ShapeAdded(ByVal Shape As IVShape)
If Not Application.IsUndoingOrRedoing Then
'Now you can perform undoable actions
IncrementModuleVar
Debug.Print "Original Do: GetModuleVar = " & GetModuleVar
'Add an undo unit that undoes/redoes that action
Application.AddUndoUnit New VBAUndoUnit
End If
End Sub
Public Sub IncrementModuleVar()
m_nCount = m_nCount + 1
End Sub
Public Sub DecrementModuleVar()
m_nCount = m_nCount - 1
End Sub
Public Function GetModuleVar() As Long
GetModuleVar = m_nCount
End Function
VBAUndoUnit 内部のコード:
Implements Visio.IVBUndoUnit
Private m_bUndone As Boolean
'm_bUndone is a flag that tells us whether an undo unit exists
'on the undo or redo stack. When you first call
'Aplication.AddUndoUnit, an instance of VBAUndoUnit is
'placed on the undo stack. Then when undo occurs, the "Do" method
'gets called and you toggle this flag and call IVBUndoManager.Add
'to get an undo unit placed on the redo stack. Then, the next time the
'Do method gets called, you toggle the flag back again and call
'IVBUndoManager.Add again to get an undo unit placed back on the undo
'stack.
'When an undo unit is on the undo stack, m_bUndone should be False
'because it is not undone yet. When your Do method gets called and the
'undo unit is transferred to the redo stack, you toggle the flag and
'm_bUndone will be True. You initialize m_bUndone as False because the
'undo unit first exists on the undo stack.
'During a rollback, (someone calls EndUndoScope(nID, *False*)),
'VBAUndoUnit's Do method will get called
'with IVBUndoManager set to Nothing. In that case, VBAUndoUnit cannot
'and should not attempt to add an undo unit to the passed in Undo
'manager. Always check for pMgr = "Is Nothing" inside of your Do
'method.
Private Sub Class_Initialize()
Debug.Print "VBAUndoUnit created..."
End Sub
Private Sub Class_Terminate()
Debug.Print "VBAUndoUnit purged..."
End Sub
Private Property Get IVBUndoUnit_Description() As String
Debug.Print "VBAUndoUnit.Description"
IVBUndoUnit_Description = "VBAUndoUnit"
End Property
Private Sub IVBUndoUnit_Do(ByVal pMgr As IVBUndoManager)
'Undo or redo, according to the state flag:
If (m_bUndone) Then
'Redo the original action:
ThisDocument.IncrementModuleVar
Else
'Undo the original action:
ThisDocument.DecrementModuleVar
End If
'Toggle the state flag:
m_bUndone = Not m_bUndone
If Not (pMgr Is Nothing) Then
'Add an an instance of VBAUndoUnit back to the opposite stack
pMgr.Add Me
Debug.Print "VBAUndoUnit.Do called with an Undo manager"
Else
'Nothing left to do -- we are in a rollback and will most
'likely soon be terminated...
Debug.Print "VBAUndoUnit.Do called WITHOUT an Undo manager"
End If
'Confirm results of undo by printing the value of the GetModuleVar
'variable
Debug.Print "After VBAUndoUnit.Do - GetModuleVar = " & _
ThisDocument.GetModuleVar
End Sub
Private Sub IVBUndoUnit_OnNextAdd()
'OnNextAdd gets called when the next unit in the same scope gets
'added to the undo stack.
Debug.Print "VBAUndoUnit.OnNextAdd"
End Sub
Private Property Get IVBUndoUnit_UnitSize() As Long
'UnitSize should return an approximate in memory size (in bytes) of
'the undo unit itself plus anything it holds on to. This allows Visio
'to use a memory size measurement to decide when to purge undo.
Debug.Print "VBAUndoUnit.UnitSize"
IVBUndoUnit_UnitSize = 4
End Property
Private Property Get IVBUndoUnit_UnitTypeCLSID() As String
'Return a CLSID string here if you think it's important to be able to
'identify your units.
'If you have several different types of units, you could return the
'same CLSID for all of them, but choose different Long IDs for each.
Debug.Print "VBAUndoUnit.UnitTypeCLSID"
IVBUndoUnit_UnitTypeCLSID = Null
End Property
Private Property Get IVBUndoUnit_UnitTypeLong() As Long
'Return a Long here if you want to identify your units. See
'discussion in UnitTypeCLSID.
Debug.Print "VBAUndoUnit.UnitTypeLong"
IVBUndoUnit_UnitTypeLong = 0
End Property