A C++ Class to manage Group Undo in MicroStation CONNECT

I want to enable MicroStation's undo logic when I change several elements in my MDL application. The application is written in C++, so I'm able to benefit from some aspects of C++ constructors and destructors.

There are two ways to accomplish this: the MicroStationAPI way and the MDL way.

MicroStationAPI Transaction Manager

#include <DgnPlatform/ITxnManager.h>

If you're using the MicroStationAPI, which provides a set of C++ classes, then you can use the Transaction Manager. The Transaction Manager includes the method pair ITxnManager::StartTxnGroup and ITxnManager::EndTxnGroup. Bentley Systems staffer Brien Bastings commented:

Be very careful not to mis-match these — that can be disastrous. Test that your tool behaves properly if the user does a Ctrl+Z in the middle of its operation, starts another tool, restarts your tool, etc.

The Transaction Manager provides more than the MDL API. See the MicroStationAPI help documentation for ITxnManager::Activate and ITxnManager::Deactivate

MDL: mdlUndo_xxx

MDL function mdlUndo_startGroup should precede one or more DGN operations on elements. The MDL documentation about that function states: Calls to mdlUndo_startGroup can be nested, but should always be matched with a corresponding call to mdlUndo_endGroup.

I'm pretty good at forgetting to do things, so that last statement raised a warning flag in my mind. How could I be sure always to call mdlUndo_endGroup? The solution, with a C++ class, is very simple: we design a class to manage undo groups by calling mdlUndo_endGroup in its destructor.

We should be able to use the class something like this …

void SomeFunctionThatModifiesElements ()
{
    UndoGroup undo;
    ...
    //  Code that modifies multiple elements
    ...
    //  UndoGroup calls mdlUndo_endGroup in its destructor
}

Here's the source code of the UndoGroup class. Feel free to add this to your own C++ projects. UndoGroup's constructor calls mdlUndo_startGroup. UndoGroup's destructor calls mdlUndo_endGroup. That's it!

UndoGroup Header File

UndoGroup.h

#if !defined(UNDO_GROUP_H_INCLUDED)
#define UNDO_GROUP_H_INCLUDED

//////////////////////////////////////////////////////////////////////
//	UndoGroup example provided by LA Solutions Ltd
//	You may freely copy this code for use in your own C++ projects
//	whether commercial or otherwise.
//	http://www.la-solutions.co.uk
//////////////////////////////////////////////////////////////////////
//	UndoGroup class is a helper to ensure that undo groups are
//	managed correctly and automatically.  Instantiate this class
//	before changing one or more elements.
//////////////////////////////////////////////////////////////////////
class UndoGroup
{
public:
    //////////////////////////////////////////////////////////////////////
    //  Construction
    UndoGroup  ();
    ~UndoGroup ();
    //////////////////////////////////////////////////////////////////////
};

#endif //   UNDO_GROUP_H_INCLUDED

UndoGroup Implementation File

UndoGroup.cpp

//    Our header
#include "UndoGroup.h"
//    MDL header files
#include <msundo.fdf>
//////////////////////////////////////////////////////////////////////
//  UndoGroup example provided by LA Solutions Ltd
//  You may freely copy this code for use in your own C++ projects
//  whether commercial or otherwise.
//  http://www.la-solutions.co.uk
//////////////////////////////////////////////////////////////////////
//  UndoGroup constructor
UndoGroup::UndoGroup      ()
{
  mdlUndo_mark ();
  mdlUndo_startGroup ();
}
//////////////////////////////////////////////////////////////////////
//  UndoGroup destructor
UndoGroup::~UndoGroup     ()
{
  mdlUndo_endGroup ();
  mdlUndo_mark ();
}
//////////////////////////////////////////////////////////////////////

UndoGroup HPP File

UndoGroup.hpp

Given the modest amount of code required to implement this class, you could merge the header and implementation files into a single .hpp file.