24 July 2008

How to: Use invisible and affined commands

In this post I would like to tell about some advanced types of commands.

Regulary, when command is commited, it is placed into the history. Users can see it in Undo list and consequently can undo it. If you use a control that I posted in the topic about history, it will look like following:



OK, it works fine. But there are two special cases when you may need different behaviour.

1) Invisible commands.
Sometimes application needs to perform a command without showing it in commands history. Someone can say: "So, I will not start the command and simply do any changes directly". But this approach is wrong because it conflicts with main statement of undo/redo: all the state data and all the changes must be registered to be rolled back consistently. Otherwise you will get inconsisitent, "partially rolledback" data causing very subtle bugs after some undo/redo operations.

Invisible commands allow to register data changes but do not show them in history.
For exapmple, let's take that user can select an object in your application. Most likely, you do not want show "Select object" as a separate operation in the history. So, you can use method

using (UndoRedoManager.StartInvisible("Select"))
{
...
}


In this case selection will not be visible in history but still be correctly undone in conjuction with previous command. In other words, it means that user can manipulate with any objects in your application and when he undo the manipulations one by one, the selection will be restored appropriatelly.

2) Affined commands.
What if user performs many very similar operations with one object? For example, when user moves an object using hot key: every keystroke of "Left Arrow" causes separate command moving the object to 5 points left. After 20 keystrokes user gets 20 commands in the history!

Some of developers think it is wrong and leads to bad user experience. And it is true in many cases. Now we can "unite" all the similar (affined) commands into single one. Affined commands must have equal captions and must manipulate with the same object.

Starting the affined command is very similar to regular one. You just need pass additional parameter:

using (UndoRedoManager.Start("Move", myObject))
{
...
}


All the other routines will be performed by DejaVu automatically. If DejaVu detects two or more affined commands following one after another, it will merge them together. If affined command comes alone, it will looks like regular command in the history.

No comments: