Delete UndoManager History after load

temporarily I’m using binary serialization for my nodesource, but it seams that the undomanager has a problem with that.

This is my load method:

if (!File.Exists(filename)) return;
using (var stream = new FileStream(filename, FileMode.Open))
    var formatter = new BinaryFormatter();
    NodesSource = (IEnumerable) formatter.Deserialize(stream);

If I now move a node once and do the undo there comes the following exception:

Exception: Exception of type System.InvalidOperationException from Northwoods.GoWPF:

at #nj.#yq.Error(String #iq)
at Northwoods.GoXam.Model.GraphLinksModelNodeData1.ChangeDataValue(ModelChangedEventArgs e, Boolean undo) at Northwoods.GoXam.Model.DiagramModel.ChangeDataValue(ModelChangedEventArgs e, Boolean undo) at Northwoods.GoXam.Model.GraphLinksModel4.ChangeDataValue(ModelChangedEventArgs e, Boolean undo)
at Northwoods.GoXam.Model.DiagramModel.ChangeModel(ModelChangedEventArgs e, Boolean undo)
at Northwoods.GoXam.Model.ModelChangedEventArgs.Undo()
at Northwoods.GoXam.Model.UndoManager.CompoundEdit.Undo()
at Northwoods.GoXam.Model.UndoManager.Undo()
at Northwoods.GoXam.CommandHandler.Undo()

I thought that UndoManager.Clear() clears the history, so I don’t know why it throws this exception.
If I do more than one change I can undo all changes but the last (right after the load) crashes.

I have no idea what might have caused that error.

Have you tried clearing the UndoManager beforehand and disabling it?

As you can see in the first post I call UndoManager.Clear() after the load.

In my Model I override ChangeDataValue and look at the Instance of the UndoManager at the moment I press Undo-Button:

There is one CompoundsEdit in the UndoManager, but in there is a MoveSelection with 10 Entries even though I moved only one Node. I analysed this and found out that there are 9 Entries (I have 9 Nodes in the diagram) with NewValue and OldValue = NULL and one Entry which is representing the move I made. Now the undo is trying to undo all 10 Entries, but only the last is valid.

I meant before, not after, the replacement of the NodesSource collection.

But really, try disabling the UndoManager by temporarily setting DiagramModel.SkipsUndoManager to true.

By the way, you can call ToString() on ModelChangedEventArgs, UndoManager.CompoundEdit, and UndoManager to help with debugging.

Calling UndoManager.Clear() or using SkipUndoManager doesn’t change anything.
I searched the sources for “MoveSelection” and found it in the DraggingTool OnMouseUp. I looked at the code and see no place where this could go wrong. Maybe the all node are in the DraggedParts-Collection, but I see no way this could be.

I write the ModelChangesEventArgs to the output window and see the same as in the debugger:

! Property : a679987f-f8fb-4c33-9eaf-641747230c10
! Property : 1335e79d-2751-4f9f-9a53-1c41c43cb4b7
! Property : 6a237e04-07fe-4e38-b581-2607e56c18c4
! Property : 3d12c1aa-f7bb-4378-9c48-60481fea04a5
! Property : eccebc58-cb5d-45fd-89d7-4aac54a126d2
! Property : 9d02d17c-2df0-40dc-a9c2-36a378624aa4
! Property : 6a04090e-a8e0-4889-96f8-9ae9882fc453
! Property : ab3736e3-88c2-4263-a384-d27422cdd9de
! Property : 4b180514-8288-4ebc-8520-4c7b5e88ed36
! Property Y: a679987f-f8fb-4c33-9eaf-641747230c10 old: 105 new: 110

These are all in the CompoundEdits with the Name “MoveSelection” even though just one node was moved. As you can see there are at first all my 9 Nodes and then the one and only move on the first node.

By the way - if I start my program without loading a nodesource via binary serialization and create Nodes with drag’n’drop from scratch the undomanager is working well!
Maybe it has something todo with the binary serialization?

It was also my first reaction that there was a problem with the deserialization, but I didn’t have any evidence to back that up, since you did say that everything seemed to work (including undo/redo) afterwards.

It’s odd that most of the ModelChangedEventArgs record a property change but the property name is empty. (I assume the GUIDs are your data keys.)

So your node location is recorded using separate X and Y properties? Check the values of the location properties on all of your new data.

You could also experiment by setting NodesSource to a programmatically constructed collection of your node data, without using deserialization, just to make sure that works. That might narrow down the problem to something involving serialization.

  1. GUIDs are the data keys
  2. All x and y are set (checked the databinding to Node.Location via snoop).
  3. Instead of deserialize I create a new ObservableCollection and put one Element in the Collection. The Node is visible in the diagram, but when I move it, I also have one entry in the CompoundEdits with just the key and no data and a second with the propertychanges.
    So I think it has nothing to do with the serialization.

But for #3, after you set NodesSource to an ObservableCollection with a single node data object, and then you move the node, does it work to undo a couple times and then redo a couple times?

You could set a breakpoint in the Changed event handler to see what is causing that nameless Property change.

Changed Event Handler of what?

of your DiagramModel

I found it!
I have a code part where I want to inform wpf that all properties maybe changed (even some calculated properties), so I wrote a method UpdateAllProperties where I want to do something like PropertyChanged(null).
So I implemented it as

public void UpdateAllProperties()
    RaisePropertyChanged("", null, null);

That causes the error.

How can I force wpf (diagram) to update all bindings without calling RaisePropertyChanged for each?

At the moment I set SkipsUndoManager = true before the call of UpdateAllPorperties and after that back to false.

I’m not sure I understand your situation.

I suppose the general answer to your question is to call GetBindingExpression and then UpdateTarget().

Another idea is to override UndoManager.SkipEvent to return true when it’s a property change with an empty string property name.

I’m no fan of multibinding, so I have a few properties that were calculated on base of other properties so if for example Visibility is based on EditMode I call OnPropertyChanged(“Visibility”) in the Setter of EditMode to force wpf to update this binding too.
How can I do this with your RaisePropertyChanged?

I “normal” MVVM-Frameworks I can call OnPropertyChanged without parameter to update all bindings. Is there a way I can do this in GoWpf?

Try using a custom UndoManager:

    protected override bool SkipEvent(ModelChangedEventArgs evt) {
      if (evt == null) return true;
      if ((int)evt.Change < (int)ModelChange.Property) return true;
      if (evt.Change == ModelChange.Property && evt.PropertyName == "") return true;
      return false;

Will that satisfy your need to call RaisePropertyChanged("", null, null)?

Yes, thank you for your help.