How to: Create and Use a Transaction Set for Multiple Undo/Redo Actions
This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies. This page may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist.
Several new Microsoft Visual Basic for Applications (VBA) members support multiple-level undo and redo functionality in Microsoft Office Project 2007. Every time an undoable action is executed, a new undo record goes onto the undo stack. You can use undo transactions to manage sets of actions in a macro. You can also use the OnUndoOrRedo event to help manage undo and redo actions.
You can set the number of undo levels (the size of the undo stack) on the General tab in the Options dialog box or by using VBA. The following code sets the number of undo levels available. The default is 20; the minimum number you can set is 1, which disables multilevel undo. You can always undo at least the most recent action, if that action is undoable.
Application.UndoLevels = 30
The Undo drop-down list can be confusing to a user who runs one macro that performs many actions that are undoable. For example, if the number of undo levels is 10, and the macro performs 20 actions, the user sees only the most recent 10 records in the Undo drop-down list. When the undo actions from a macro are mixed with undo actions from the user, it can be difficult or impossible to tell which records should be undone to roll back the macro.
The solution is to group every undo record from a macro into a batch, which is displayed as a single named action in the Undo drop-down list. The new Application methods OpenUndoTransaction and CloseUndoTransaction group the undoable actions into a batch with a name, for example, "My macro." The user can undo all of the batched actions at once by clicking the single name "My macro" in the undo list. When the batch is undone, the user can find the "My macro" name in the redo list, and re-execute the macro.
For a simple code example, see Creating and Using an Undo Transaction Set. For an event handler for undo and redo events, see Creating and Using an OnUndoOrRedo Event Handler.
You can use the following methods, property, and event of the Application object to programmatically manage undo and redo actions.
OpenUndoTransaction method
CloseUndoTransaction method
EditUndo method
EditRedo method
GetUndoListCount method
GetUndoListItem method
GetRedoListCount method
GetRedoListItem method
Undo method
Redo method
UndoClear method
UndoLevels property
IsUndoingOrRedoing method
OnUndoOrRedo event
In addition, the OptionsGeneral method adds the MaxUndoRecords parameter that supports the Undo levels setting in the Options dialog box. For signatures and descriptions of the members, see VBA Help in the Project 2007 client (or see Office Online), or Table 2 in Tables of VBA Object Model Changes.
Creating and Using an Undo Transaction Set
Macros in Project 2007 add actions to the undo stack. You can easily use the Visual Basic Editor to compare a macro that does not batch actions with a macro that uses an undo transaction set.
To compare macros that batch and do not batch actions:
Open the Visual Basic Editor in Project 2007. Add the following macro in the ThisProject code module. The macro does not batch actions.
Sub CreateSixTasks() Dim i As Integer For i = 1 To 6 ActiveProject.Tasks.Add "T " & i Next End Sub
Manually create two tasks named M1 and M2, and then run the CreateSixTasks macro. Figure 1 shows the result in the Undo drop-down list.
Figure 1. Undo list showing mixed user and macro actions that are not in a batch
Most macro actions do not have a descriptive label. In Figure 1, for example, the manual task entries have labels that describe the user action but the macro actions are simply labeled Action.
Open the Visual Basic Editor again and add the following macro that makes an undo transaction set. The macro creates six new tasks within the undo set. New object model features in Project 2007 are shown in bold font.
Sub CreateTasksWithUndoTransaction() ActiveProject.Tasks.Add "Task outside transaction" Application.OpenUndoTransaction "Create 6 tasks" Dim i As Integer For i = 1 To 6 ActiveProject.Tasks.Add "UndoMe " & i Next Application.CloseUndoTransaction End Sub
Note A macro can perform actions outside of an undo transaction or include multiple named undo transactions. However, you cannot nest one undo transaction within another.
After you run the CreateTasksWithUndoTransaction macro, open the drop-down list for Undo Last Command on the Standard toolbar to see the undo transaction label (Figure 2).
Figure 2. Using an undo transaction set for a batch of undo actions
The Edit menu also shows Undo Create 6 tasks at the top of the menu. If you put the statement that adds the "Task outside transaction" task below the CloseUndoTransaction statement, the Edit menu would show only Undo Action at the top. However, you can press CTRL+Z multiple times to undo multiple actions in the stack.
You can also use OpenUndoTransaction and CloseUndoTransaction on single commands. A macro can re-label a command by creating a batch transaction of one action with a new label. For example, the Project Guide in Project 2007 uses batch transactions to re-label some commands for undo actions.
Creating and Using an OnUndoOrRedo Event Handler
OpenUndoTransaction takes two parameters. The first parameter is a String for the batch label. A user sees the batch label in the undo list and as the undo command at the top of the Edit menu. The optional second parameter is a GUID that helps extensions manage undo and redo actions.
When a batch transaction is undone or redone, Project includes the batch label and GUID as parameters of the OnUndoOrRedo event. An extension can trap the event, listen for the GUID of interest, and take appropriate action.
To create an OnUndoOrRedo event handler:
Add a class module to the VBA project. In the Visual Basic Editor, right-click VBAProject, click Insert, and then click Class Module to create a class named Class1. Rename the class module in the Properties pane, if you want. In the following examples, the class is named TestClass.
Add the global declarations and class constructor. Because OnUndoOrRedo is an Application event, declare an Application object using the WithEvents keyword. In this case, the Class_Initialize method doesn't have any initialization statements. The global variable myTaskGuid is declared for use in the example in later steps.
Option Explicit Public WithEvents oApp As Application Private myTaskGuid as String Private Sub Class_Initialize() ' Add class initialization statements here, if needed End Sub
Create the event handler. After you declare the oApp object, select it in the object drop-down list in the Visual Basic Editor. All of the Application events are now in the drop-down list on the right. Select OnUndoOrRedo in the list to generate the following event handler code.
Private Sub oApp_OnUndoOrRedo(ByVal bstrLabel As String, _ ByVal bstrGUID As String, _ ByVal fUndo As Boolean) ' Add code in step 4 here. End Sub
Add code within the oApp_OnUndoOrRedo event handler subroutine. For example, add the following code as a test.
Dim action As String Dim msg As String If (bstrGUID = myTaskGuid) Then If (fUndo) Then action = "Undo" Else action = "Redo" End If msg = action & " action on '" & bstrLabel & _ "' transaction set." MsgBox msg, , "MLU Info" End If
Add any other class methods, properties, and events you need. For example, add the following method to create an undo transaction set that performs the same task as the previous CreateTasksWithUndoTransaction macro. In this case, you need to add the optional tGuid parameter.
Public Sub CreateTasksWithTransactionGuid(label As String, tGuid As String) Dim oApp As New Application ActiveProject.Tasks.Add label & "outside transaction" myTaskGuid = tGuid oApp.OpenUndoTransaction label, tGuid Dim i As Integer For i = 1 To 6 ActiveProject.Tasks.Add label & i Next oApp.CloseUndoTransaction End Sub
In the ThisProject code module, or another module, add the following declaration and macro to test the event handler.
Option Explicit Private tClass As New TestClass Sub TestUndoEvent() Dim myTaskGuid As String Set tClass.oApp = Application myTaskGuid = "{CF8864AC-3C04-4d12-AE34-A65AB4C70783}" tClass.CreateTasksWithTransactionGuid "TestClass Tasks", myTaskGuid End Sub
Test the event handler.
Run the TestUndoEvent macro.
Check the list of tasks and undo events in Project.
Undo the TestClass Tasks item in the undo list.
You should see the MLU Info message box with the message "Undo action on 'TestClass Tasks' transaction set."
Click the Redo drop-down list and select the TestClass Tasks item.
You should see the corresponding redo message box.
Example
The following code example shows all of the code in the ThisProject module and in the TestClass class module.
' _______________________________________
' ThisProject standard module
' _______________________________________
Option Explicit
Private tClass As New TestClass
Sub CreateSixTasks()
Dim i As Integer
For i = 1 To 6
ActiveProject.Tasks.Add "T " & i
Next
End Sub
Sub CreateTasksWithUndoTransaction()
Dim myTaskGuid As String
ActiveProject.Tasks.Add "Task outside transaction"
Application.OpenUndoTransaction "Create 6 tasks"
Dim i As Integer
For i = 1 To 6
ActiveProject.Tasks.Add "UndoMe " & i
Next
Application.CloseUndoTransaction
End Sub
Sub TestUndoEvent()
Dim myTaskGuid As String
Set tClass.oApp = Application
myTaskGuid = "{CF8864AC-3C04-4d12-AE34-A65AB4C70783}"
tClass.CreateTasksWithTransactionGuid "TestClass Tasks", myTaskGuid
End Sub
' _______________________________________
' TestClass class module
' _______________________________________
Option Explicit
Public WithEvents oApp As Application
Private myTaskGuid As String
Public Sub CreateTasksWithTransactionGuid(label As String, tGuid As String)
Dim oApp As New Application
ActiveProject.Tasks.Add label & "outside transaction"
myTaskGuid = tGuid
oApp.OpenUndoTransaction label, tGuid
Dim i As Integer
For i = 1 To 6
ActiveProject.Tasks.Add label & i
Next
oApp.CloseUndoTransaction
End Sub
Private Sub oApp_OnUndoOrRedo(ByVal bstrLabel As String, _
ByVal bstrGUID As String, _
ByVal fUndo As Boolean)
Dim action As String
Dim msg As String
If (bstrGUID = myTaskGuid) Then
If (fUndo) Then
action = "Undo"
Else
action = "Redo"
End If
msg = action & " action on '" & bstrLabel & "' transaction set."
MsgBox msg, , "MLU Info"
End If
End Sub
Private Sub Class_Initialize()
' Add class initialization statements here, if needed
End Sub
In the TestUndoEvent method, use your own GUID, or it will not be unique. Although we cannot vouch for the code, you can find sample VBA code that generates GUIDs on the Internet. Search for "generate VBA Guid".