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:

  1. 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
    
  2. 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

    Undo list showing mixed user and macro actions

    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.

  3. 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
    
    NoteNote

    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.

  4. 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

    Using an undo transaction set

    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:

  1. 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.

  2. 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
    
  3. 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
    
  4. 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
    
  5. 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
    
  6. 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
    
  7. Test the event handler.

    1. Run the TestUndoEvent macro.

    2. Check the list of tasks and undo events in Project.

    3. 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."

    4. 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".

See Also

Reference

Tables of VBA Object Model Changes