I have just started using PostSharp, currently for the Undo/Redo functionality that is provided but probably for a lot more in the future.
I am wondering if there is any way to implement a nested set of operations within our application.
So I have my object model in the main application, I then launch an editor window to modify a list of messages. I can correctly have PostSharp monitor all of the individual changes that I make to each message and allow the user to undo those changes. So far so good.
What I then want is to allow the user to exit the editor and now have a single operation on the Undo stack - "Undo Message Changes" or something similar. This would involve taking all of the individual changes and merging them in to a single operation when the editor is dismissed. I am currently investigating the OperationScope methods but I'm not sure I am going in the right direction. When within a scope the operations aren't added immediately to the undo stack, they're only added when the scope is closed which isn't quite what I want.
I have included a very cut down example of my test code and some comments on what I expect at different points. It might be that what I want isn't possible but hopefully someone will have some recommendations!
private void btnShowEditor_Click(object sender, EventArgs e)
{
// Nothing on the undo stack at this point
LaunchEditorWindow();
// I would like one composite operation at this point that can undo all of the changes made within LaunchEditorWindow
}
// Imagine this code is inside an editor window and the changes are being done by the user
private void LaunchEditorWindow()
{
_network.Messages.Add(new Message() { Name = "Message 1", BitLength = 1 });
// I would expect there to be three undo operations available here
System.Diagnostics.Debug.WriteLine(RecordingServices.DefaultRecorder.UndoOperations.Count);
_network.Messages.Add(new Message() { Name = "Message 2", BitLength = 2 });
// 6 operations now
_network.Messages.Add(new Message() { Name = "Message 3", BitLength = 3 });
// etc etc
_network.Messages.Add(new Message() { Name = "Message 4", BitLength = 4 });
System.Diagnostics.Debug.WriteLine(RecordingServices.DefaultRecorder.UndoOperations.Count);
}
PostSharp undo/redo has a concept of nested operations, but this concept is not exposed to the public API: it is used internally to support atomic operations. The rationale behind not exposing the tree structure to users is that users generally expect a linear structure.
OperationScope will not allow you to expose a hierarchical structure to your users. Scopes can be hierarchical but operations are automatically "flattened", i.e. a new scope does not always result in a new operation (the parent operation is reused if it exists unless the new scope is atomic).
You can investigate restore points (see http://doc.postsharp.net/m_postsharp_patterns_recording_recorder_addrestorepoint_762235a2).
It seems that what you want is the ability to merge all operations from a restore point to the current point into a new operation of a given name. The current API does not allow to do that. I added the feature request http://postsharp.uservoice.com/forums/4949-feature-requests/suggestions/7776894-undo-redo-api-to-merge-operations and I encourage you to vote for it.
Note that you could implement the feature yourself by creating a custom "view model" on the top of Recorder.UndoOperations, which would display the operations using the level of aggregation that is meaningful to you. This is a sensible options because it would give you full control over the user's view of the undo/redo system, which is basically what you want. However it means you would have to create your own undo/redo buttons.