UndoManager.Clear and UndoCommand CanExecute

I use UndoManager.Clear when loading or saving a file, but the UndoButton in my ToolBar bound to the UndoCommand of the CommandManager is still enabled.
How can I fix this?

How do you define your “undo” button?

Calling UndoManager.Clear will definitely cause UndoManager.CanUndo to return false, so CommandHandler.CanUndo will also be false. Could you call a RaiseCanExecuteChanged method on the Command?


<dxb:BarButtonItem x:Name="Undo" Glyph="{StaticResource undoDrawingImage}" Command="{Binding UndoCommand}" Hint="Undo" />
<dxb:BarButtonItem x:Name="Redo" Glyph="{StaticResource redoDrawingImage}" Command="{Binding RedoCommand}" Hint="Redo"/>

And I use the Diagram.CommandHandler.UndoCommand for the Binding.

I tried

Diagram.CommandHandler.UndoCommand. <-- but there is no RaiseCanExecuteChanged

What did I miss?

The Binding is working well using undo/redo - the problem occurs only one UndoManager.Clear.

Maybe you could raise the CanExecuteChanged event yourself.

    if (cmd.CanExecuteChanged != null) cmd.CanExecuteChanged(myDiagram, EventArgs.Empty);

The Event CanExecuteChanged can only appear on the left hand side of a += or -=

by the way even a CommandManager.InvalidateRequerySuggested(); doesn’t work.

I have no Idea.

In the MainView there are the two Buttons. The Buttons are bound to ICommands in the MainViewModel.
A GoWpf Diagram is hosted in an usercontrol which represent the DiagramView. This DiagramView is hosted in the MainView. The DiagramViewModel is sending your Undo- and RedoCommand to the MainView.
As I wrote - all is working well clicking undo-/redo Buttons. The Button where enabled and disabled from the state of the your undobuffer. But then I call UndoManager.Clear and the Buttons stay as they where in this moment.

As a final suggestion, I was going to mention CommandManager.InvalidateRequerySuggested. Oh well. I don’t know either.

If you haven’t already checked that CommandHandler.CanUndo is false immediately after you call UndoManager.Clear, that would confirm that updating the command state is the only thing that needs to be done.

But maybe there’s a completely different possibility – don’t call UndoManager.Clear at all. Instead either replace the Diagram.Model with a new model (that will have a new and empty UndoManager) OR replace the DiagramModel.UndoManager with a new UndoManager.

CommandHanlder.CanUndo is false - I checked that.
If I replace the UndoManager, will the commands be the same?

UndoManager = new UndoManager doesn’t change anything.

It’s still an update problem. One little move of a diagram-node refreshes the buttons.

After calling UndoManager.Clear, try toggling twice the value of Diagram.AllowEdit or another one of the “Allow…” properties of Diagram that you aren’t really using.

Each time any of those “Allow…” properties changes value it will update all of the Commands.

I toggle the AllowRotate-Value and this works like a charm.

Thank you.

But where is the real problem? Is this really the solution?

Can I override the Clear() Method in a CustomUndoManager and do the toggle to ensure this happens everytime someone call UndoManager.Clear? Does the UndoManager have a reference to the Diagram?

Good – it’s useful to know that that work-around was successful for you.

You are right – that is not a general solution given that there can be multiple Diagrams showing many DiagramModels sharing the same UndoManager. The only general solution is to send a Changed event (ModelChangedEventArgs) to the models and then to their diagrams.

So something like this on a subclass of UndoManager:

    public override void Clear() {
        foreach (var m in this.Models) {
            var e = new ModelChangedEventArgs() { Model = m, Change = ModelChange.ChangedName };

Caution: I haven’t tried this.

This is working after saving the diagram, but after loading a file in the diagram, the undo-button is active a if I click it an exception is thrown. Edit: No sorry the exception is thrown also after the asve, changing anything and doing the click on undo.

I do it the other way since I have only one diagram and two parts (load/save) where doing the clear.

Exception: Exception of type System.InvalidOperationException from Northwoods.GoWPF:
ChangeModelState did not handle ModelChange.ChangedName

at #nj.#yq.Error(IDiagramModel #tk, String #iq)
at Northwoods.GoXam.Model.DiagramModel.#ij(ModelChangedEventArgs #p, Boolean #jj)
at Northwoods.GoXam.Model.GraphLinksModel`4.#ij(ModelChangedEventArgs #p, Boolean #jj)
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()
at #db.#cb.#bb(Object #6)
at DevExpress.Xpf.Bars.BarItem.ExecuteCommand()

OK. We’ll improve this for the next release, so that no override of Clear or trying to raise or handle ModelChangedEventArgs is needed.

Nice to hear…