4 September 2008

How To: Use notifications of property changed

There are two ways to get notified of changes made withing a command:

  1. CommandDone event (in UndoRedoManager)
  2. Changed event (in particular UndoRedo property, list or dictionary)

CommandDone event is fired after any command has been commited/undone/redone. It is fired once per command despite how many changes were in the command.

Changed event provides much higher level of granularity. It is fired when particular property/list/dictionary was changed during a command.

In this post I will tell more about the Changed event. This event provides very precise control on what data notifications you receive. Subscribtion is fairly simple:



class DataClass
{
internal readonly UndoRedo<int> prop1 = new UndoRedo<int>(0);
public int Prop1
{
get { return prop1.Value; }
set { prop1.Value = value; }
}
public Subscribe()
{
prop1.Changed +=
new EventHandler<MemberChangedEventArgs>(prop1_Changed);
}
}


Besides of precision, Changed event guaranties data consistency for subscriber. In other words, subscriber may always assume that all data is in consistent state despite on order of how data was changed. For example, we subscribe on Prop1 changes. The point is that our handler may rely that Prop2 is consistent too even if it was changed later in the command.

This magic happens because Changed event is not fired immediately but waits untill the command is commited. And this is the most important thing you have to understand: firing of Changed event is postponed to the point where all data is in consistent state and commited.

This advantage allows you to selectively update UI layer with confidence that changed property and any other data properties can be correctly displayed. So, you can write very intelligent and reliable MVC/MVVM pattern even for complex cases.

Another notes are:

  • If command was cancelled, there will not be any Changed event;
  • If a property was changed ten times during the command, Changed event will be fired once for this property when command commited;
  • Changed event will also be fired when the same command is Undone/Redone;
  • You can assign Owner and Name to the property. It usefull when one handler subscribed on several properties at once:



prop1.Owner = this;
prop1.Name = "Prop1";
prop2.Owner = this;
prop2.Name = "Prop2";
...
void prop_Changed(object sender, MemberChangedEventArgs e)
{
if (e.Member.Name == "Prop1")
{...}
}

3 September 2008

How to: Use nested commands

A new subsequent command can be started while another parent command has already been started:


using (UndoRedoManager.Start("My Root Command"))
{
using (UndoRedoManager.Start("My Nested Command"))
{
...
}
...
}



You can put part of a job inside the nested command and then cancel it if error occured without canceling parent command.

Commands hierarchy can be of any depth. Some of commands can be commited and some of them can be rolled back. All the changes made in these commands will be put in the history when root command commited.

So, despite the number of nested commands you will see the only one root command in history containing all changes accumulated.

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.

How To: Create command and undo/redo it

I hope you have read previous posts where we declare undoable data members.
Now, lets do our first command:



public void DoCalculations(MyClass c, int i)
{
UndoRedoManager.Start("Change count");
c.Count = i * c.Count;
UndoRedoManager.Commit();
}


In this sample we changed only one property but real command can contain any number of changes of any properies of any classes. All these changes will be commited with Commit() method. If command was unsuccessful for some reason, the method Cancel() should be used instead. In this case all the changed data will rollback at the exactly same state it was at Start point.



public void DoCalculations(MyClass c, int i)
{
UndoRedoManager.Start("Change count");
c.Count = i * c.Count;
if (c.Count < 1000)
UndoRedoManager.Commit();
else
UndoRedoManager.Cancel();
}


As I noted words "commit" and "rollback", you probably think that DejaVu is one more transactional engine. Not so. The real difference is that history of commands is available all the time and we can undo them at any depth and redo some of them again.

It is also vitaly important to understand that your logic does not run any code again when a command is redone. The data state is solely returned into previous state. So, your business logic need know nothing about undo/redo activities.


How does undo/redo look like in the code? "Here We Have Idaho":



...
UndoRedoManager.Undo();
...
UndoRedoManager.Redo();


I hope you find whole this stuff simple. Meanwhile, I would like to return to the very first sample in this post. The thing is that... I do not know how to say you...
It's... It's not very reliable... The problem is that if we fail inside the command by exception, the command will be never commited or canceled. But no worries. The code snippet below shows the best practice how to deal with commands in most cases:


using (UndoRedoManager.Start())
{
...
UndoRedoManager.Commit();
}

This sample undoes last command and then brings all changes back.


Now, if exception is thrown inside the command, it will be automatically rolled back at the closing bracked of using statement. On other hand, if command successfully comes to end, it will be commited.

My practice shows that its a very reliable way to build fail proof application. Data state is safeguarded against falling into incorrect state. After command completed or unpredictably failed, data is always in correct state as it designed.

How To: Declare undoable list and dictionary

For the moment of writing this topic DejaVu supports two types of data collections: list and dictionary.

They are implemented in classes DejaVu.Collections.Generic.UndoRedoList<T> and DejaVu.Collections.Generic.UndoRedoDictionary<T>.
These classes have exactly the same public interface as standard classes List<T> and Dictionary<T> correspondently but with undo/redo engine under the hood.

Declaration of list/dictionary propery is farly simple:

using Dejavu;
...
class MyClass2
{
private readonly UndoRedoList<MyClass> myclasses = new UndoRedo<MyClass>();
public UndoRedoList<MyClass> MyClasses
{
get { return myclasses ; }
set { myclasses = value; }
}
}


As you can see property returns an list of type UndoRedoList<MyClass>. You can add/remove items in this list as you did ever before but when time comes, all these changes can be undone or redone in a single bunch.

In next posts I will eventually show how you can organize your changes into commands and how to make them undo/redo.

How To: Declare undoable data

In this topic I will show the basis of Dejavu: declaring data member that can be subject of undo/redo.

There are many kinds of data: primitive types, references to complex types (objects), collections. All these data can be made "undoable".

Lets start from primitive type. That's how you can declare int property in your class:

using Dejavu;
...
class MyClass
{
private readonly UndoRedo<int> count = new UndoRedo<int>(5);
public int Count
{
get { return count.Value; }
set { cound.Value = value; }
}
}


As you see there are minor internal differences from classic property declaration. Public contract does not change. So, nothing changed for external developer. But the same time, we have got undoable property declaration!

Reference data type can be declared very similar:

using Dejavu;
...
class MyClass2
{
private readonly UndoRedo<MyClass> myClass = new UndoRedo<MyClass>();
public int MyClass
{
get { return myClass.Value; }
set { myClass.Value = value; }
}
}


The initial value contained in myClass will be null because we used contsructor with no parameters. In the previous code snippet our int property will be preset with initial value 5.

In next topic I will show how to get undoable collections.

15 July 2008

Features of DejaVu project

Well, in this post I will briefly enumerate features of DejaVu:
  • Undo/Redo for operations executed
    You can do any manipulations with your data and then rollback them (Undo) and optionaly do again them (Redo). DejaVu will guarantie data consistency and you can be sure that your data model has completely the same state as required.
  • History of commands
    You can get whole history of commands, to show it to user. Thus, user can select how many commands he want undo or redo.
  • Isolated areas
    There are some cases when changes must be accumulated indepentently. E.g. multi-document application allows undo changes in Document #1 without affecting document 2,3...n. So, DejaVu allows assign one area for each document and manage them independently.
  • User actions log
    You can get log of all actions user done in the application. Its especially useful when you want get a report after application crash: what the actions user done before? what did the commands lead to crash?
  • Recovering application state
    If you get an exception inside a command, DejaVu can help you automatically recover the data state. If you follow recomendations your data will return to consistent state and application will proceed its life.

About DejaVu project

DejaVu project emerged as a response on the challenge of implementing Undo/Redo feature in my .NET applications.

Typical application with Undo/Redo have rich user interface with some sort of graphical designer. Designers are stuffed with many elements that can be dragged, streatched and manipulated in other different manner. It's vitaly important for positive user experience to provide Undo/Redo feature that will work consistently throughout the application.

And here is the challenge because it quite difficult to write code for N types of objects that support M operations that can be executed and rolled back in arbitrary order.

After some work I have developed component DejaVu and posted it on http://codeplex.com/dejavu.
I maintain this project in my spare time, you can check it for updates from time to time. Its written on C#, so I hope the .NET community will enjoy this open source project.

I also wrote a review on CodeProject.com site. I'd recommend read it for all who want get complete walkthough on the subject.