Two question about Undo feature

WPF V1.2.2.4

Hello,

1) I have implemented model change notifications as explained in the ‘Updating a database’ section of the intro document. To make it work properly, for the ModelChange.FinishedUndo case, I must myself send the ‘reverse’ command; For Example if Change is RemovedNode, I must send an AddedNode change. Is this correct or have I missed something ?



2) Probably because the Location property is not binded, when undoing a node deletion, the node is restored at some invisible location. Later, when I add another node (drag-drop) the restored node became visible in the top left corner of the diagram.



In a later stage of my project I must allow a node to be visible in different diagrams at location per diagram. For now is enough to have a restored node to be displayed in some visible constant location in the diagram.



Thanks





  1. Yes, that’s right, and (if it matters) you need to do them in reverse order. And upon a FinishedRedo, you need to do the same things that happened originally, and in the same order.

  2. How do the nodes get locations in your app? If you are depending on layout to assign those locations, and if you are not “remembering” them via two-way data-binding, then you need to assign them yourself somehow.

You could just perform another layout. Or you could go ahead and data-bind the go:Node.Location after all – you don’t need to save the locations in your database if you don’t want to.

After refactoring of my application, Model change notifications doesn’t work anymore and after one full day of no luck search I ask you some help.



The symptom is that the e.OldValue is always null when the e.Change is CommitedTransaction.



1) The model is HasUndoManager = false to make it simpler.



2) A part of my handler code:



private void OnGraphModelChanged(object sender, ModelChangedEventArgs e)

{

Console.Out.WriteLine(“OnGraphModelChanged [” + e.Change + “] Data[” + e.Data + “] NewParam[” + e.NewParam + “] PName[” + e.PropertyName + “] NewValue[” + e.NewValue + “] OldValue[” + e.OldValue + “]”);



if (e.Change == ModelChange.CommittedTransaction)

{

var l_CompoundEdit = e.OldValue as UndoManager.CompoundEdit;



if (l_CompoundEdit == null) return;



foreach (IUndoableEdit l_Edit in l_CompoundEdit.Edits)

{

var l_EditEvent = l_Edit as ModelChangedEventArgs;



if (l_EditEvent == null) continue;



CommonStateViewModel l_ViewModelState = null;

if (l_EditEvent.Data is GraphStateViewModel)

{

l_ViewModelState = (l_EditEvent.Data as GraphStateViewModel).StateViewModel;

}

_NotifyModelChange(l_EditEvent);

}

}

else





3) The trace when I drop a node from the palette:



OnGraphModelChanged [StartedTransaction] Data[Layout] NewParam[] PName[] NewValue[] OldValue[]

OnGraphModelChanged [CommittedTransaction] Data[Layout] NewParam[] PName[] NewValue[] OldValue[]

OnGraphModelChanged [AddedNode] Data[00000000-0000-0000-0000-000000000001] NewParam[] PName[] NewValue[] OldValue[]

OnGraphModelChanged [Property] Data[00000000-0000-0000-0000-000000000001] NewParam[] PName[Location] NewValue[40.75,7.7] OldValue[Non Numérique,Non Numérique]

OnGraphModelChanged [RemovingNode] Data[00000000-0000-0000-0000-000000000001] NewParam[] PName[] NewValue[] OldValue[]

OnGraphModelChanged [RemovedNode] Data[00000000-0000-0000-0000-000000000001] NewParam[] PName[] NewValue[] OldValue[]

OnGraphModelChanged [StartedTransaction] Data[Drop] NewParam[] PName[] NewValue[] OldValue[]

OnGraphModelChanged [AddedNode] Data[00000000-0000-0000-0000-000000000001] NewParam[] PName[] NewValue[] OldValue[]

OnGraphModelChanged [Property] Data[00000000-0000-0000-0000-000000000001] NewParam[] PName[Location] NewValue[40.75,7.7] OldValue[Non Numérique,Non Numérique]

OnGraphModelChanged [CommittedTransaction] Data[ExternalCopy] NewParam[] PName[] NewValue[] OldValue[]



Thanks

If you don’t have a DiagramModel.UndoManager, then it won’t be recording all of the changes (ModelChangedEventArgs), so there won’t be any CompoundEdit available in the CommittedTransaction event.

So you either need to set DiagramModel.HasUndoManager to true, or you need to create an UndoManager and set DiagramModel.UndoManager.

Well… I have tested to suppress the UndoManager in the UpdateDemo sample and it works (not the undo redo features, of course)



model.NodesSource = nodes;

model.LinksSource = links;

model.Modifiable = true;

//model.HasUndoManager = true;

model.Changed += model_Changed;



Well… no, i am stupid. your handler is different in the sample…

Yes, if you don’t set model.HasUndoManager to true, model.Changed events and transactions still occur.

But there won’t be any record of those Changed events.

Ok, back to the problem, with the UndoManger things go better but with a strange behaviour.



This is the trace when I drop a node from the palette:

OnGraphModelChanged [StartedTransaction] Data[Layout] NewParam[] PName[] NewValue[] OldValue[]

OnGraphModelChanged [CommittedTransaction] Data[Layout] NewParam[] PName[] NewValue[] OldValue[]

OnGraphModelChanged [AddedNode] Data[00000000-0000-0000-0000-000000000001] NewParam[] PName[] NewValue[] OldValue[]

OnGraphModelChanged [Property] Data[00000000-0000-0000-0000-000000000001] NewParam[] PName[Location] NewValue[174.75,211.7] OldValue[Non Numérique,Non Numérique]

OnGraphModelChanged [Property] Data[00000000-0000-0000-0000-000000000001] NewParam[] PName[Location] NewValue[222.75,268.7] OldValue[174.75,211.7]

OnGraphModelChanged [StartedTransaction] Data[Layout] NewParam[] PName[] NewValue[] OldValue[]

OnGraphModelChanged [CommittedTransaction] Data[Layout] NewParam[] PName[] NewValue[] OldValue[]

OnGraphModelChanged [Property] Data[00000000-0000-0000-0000-000000000001] NewParam[] PName[Location] NewValue[227.75,272.7] OldValue[222.75,268.7]

OnGraphModelChanged [Property] Data[00000000-0000-0000-0000-000000000001] NewParam[] PName[Location] NewValue[228.75,273.7] OldValue[227.75,272.7]

OnGraphModelChanged [Property] Data[00000000-0000-0000-0000-000000000001] NewParam[] PName[Location] NewValue[226.75,273.7] OldValue[228.75,273.7]

OnGraphModelChanged [RemovingNode] Data[00000000-0000-0000-0000-000000000001] NewParam[] PName[] NewValue[] OldValue[]

OnGraphModelChanged [RemovedNode] Data[00000000-0000-0000-0000-000000000001] NewParam[] PName[] NewValue[] OldValue[]

OnGraphModelChanged [StartedTransaction] Data[Drop] NewParam[] PName[] NewValue[] OldValue[]

OnGraphModelChanged [AddedNode] Data[00000000-0000-0000-0000-000000000001] NewParam[] PName[] NewValue[] OldValue[]

OnGraphModelChanged [Property] Data[00000000-0000-0000-0000-000000000001] NewParam[] PName[Location] NewValue[226.75,273.7] OldValue[Non Numérique,Non Numérique]

OnGraphModelChanged [CommittedTransaction] Data[ExternalCopy] NewParam[] PName[] NewValue[] OldValue[CompoundEdit: ExternalCopy 7 edits]

OnGraphModelChanged [StartedTransaction] Data[Layout] NewParam[] PName[] NewValue[] OldValue[]

OnGraphModelChanged [CommittedTransaction] Data[Layout] NewParam[] PName[] NewValue[] OldValue[]



There is a strange RemovedNode that appends before the AddedNode, even if the removed node is not in a transaction.



Some idea ?



That’s the temporary node that is being dragged during the drag-and-drop, before the drop.

The Model.Changed events are needed so that all Diagrams using the model can be kept up-to-date.

But not all Changed events are of equal importance or meaning. As you say, the temporary events are not recorded in the UndoManager.CompoundEdit that you get when a transaction is committed. Neither are the transaction or undo events themselves.

Ok, but as see in the trace:



OnGraphModelChanged [CommittedTransaction] Data[ExternalCopy] NewParam[] PName[] NewValue[] OldValue[CompoundEdit: ExternalCopy 7 edits]



There is 7 edits when the CommitTransition appends, that means the Removed node event is in the edits. If I a simply loop through the edits, I will send a faulty RemovedNode to my database.



In fact I must loop starting from the StartedTransaction event to have only the ‘True’ events ? But the StartedTransaction event is not in the edits. How to do ?

That’s odd. When I modify the FlowChart sample by adding a Changed event handler on the model of the main Diagram, which checks for the ModelChange.StartedTransaction and CommittedTransaction cases and casts the ModelChangedEventArgs.OldValue to an UndoManager.CompoundEdit, and which then loops over the CompoundEdit.Edits and prints each one, everything seems fine to me. Here’s my output when dragging the “DataBase” node from the Palette to the Diagram:

model.Changed: StartedTransaction Drop
model.Changed: CommittedTransaction ExternalCopy
! AddedNode: DataBase
! Property Location: DataBase old: 0,512.28 new: 56,454.04
model.Changed: StartedTransaction Layout
model.Changed: CommittedTransaction Layout
model.Changed: StartedTransaction DelayedRouting
model.Changed: CommittedTransaction DelayedRouting
! Property Points: 0(0) --> 6(1)
old: System.Collections.Generic.List1[System.Windows.Point] new: System.Collections.Generic.List1[System.Windows.Point]

So I wonder why you’re getting the ModelChange.RemovedNode case event recorded in the UndoManager.

But even if you do, you can recognize that the “RemovedNode” or “RemovingNode” changes will either be no-ops because the node data hadn’t been added or will just make sure that the temporary node data doesn’t persist in your database.

Well I have found something:

The problem appends when I drag a Group node from the palette. Dragging a non-group node works (no deleted node event emitted).



I have a drastically reduced version of my application that show the problem if you want.





I just tried having a node data representing an empty group in the Palette. Dragging it to the Diagram resulting in the same behavior as with a regular node: there was no “Removing Node” change recorded by the UndoManager, just an “Added Node” change.

So if you can send your test app to us by email, I can take a look at it later. Or post it somewhere I can download it.