MVVM RelayCommand CanExecute
Asked Answered
B

4

9

I'm implementing an RelayCommand with an execute and an canExecute part. The RelayCommand works when it is without the canExecute part, however when I add the canExecute part, the command locks the button. The RelayCommand only checks whether or not the button can be executed as long as the CanExecute part is true. Once the canExecute part becomes false, the button can no longer be clicked, even if it is supposed to. How do I make sure that every time I click on the button it controls whether or not it can be executed, and doesn't lock it forever, once it cannot be executed?

RedoCommand = new RelayCommand(undoRedoController.Redo,undoRedoController.CanRedo);

   public bool CanRedo()
    {
        redoStack.Count();
        redoStack.Any();
        return redoStack.Any();
    }

    public void Redo()
    {
        if (redoStack.Count() <= 0) throw new InvalidOperationException();
        IUndoRedoCommand command = redoStack.Pop();
        undoStack.Push(command);
        command.Execute();
    }


 public class UndoRedoController
{
    private static UndoRedoController controller = new UndoRedoController();

    private readonly Stack<IUndoRedoCommand> undoStack = new Stack<IUndoRedoCommand>();
    private readonly Stack<IUndoRedoCommand> redoStack = new Stack<IUndoRedoCommand>();

    private UndoRedoController() { }

    public static UndoRedoController GetInstance() { return controller; }
Bondage answered 14/10, 2013 at 16:1 Comment(3)
Can you post your implementation of RelayCommand which you are using.Fulbert
@RohitVats the implementation of the RelayCommand, is implemented as shown above, or are you talking about the xaml parrt?Bondage
Make sure you're using the CommandWPF namespace because the RelayCommand CanExecute is broken in the Command namespace. See blog.jsinh.in/…Fluoro
V
6

For some reason you have to do the following:

public RelayCommand RedoCommand{
     get;
     set;
}

you can also put private before set optional, depending on your access level. Then you do

RedoCommand = new RelayCommand(() => undoRedoController.Redo(), () => undoRedoController.CanRedo());

Now your able to call RedoCommand.RaiseCanExecuteChanged(); And everything works.

Variate answered 22/10, 2013 at 8:28 Comment(4)
I get a compiler error when trying that example. If one removes the "() =>" in the RelayCommand ctor and the parens after the method names, it works fineFluoro
Okay, can't explain that. I have the above working. If it was helpful then please up vote :)Variate
Could be the version of MVVM Light, not sureFluoro
Make sure you're using the CommandWPF namespace because the RelayCommand CanExecute is broken in the Command namespace. See blog.jsinh.in/…Fluoro
A
16

There has been a hiatus with MVVMLight due to the fact that after the .NET 4.5 update the CommandManager no longer fires the can execute check. This has since been solved. Instead of including the GalaSoft.MvvmLight.Command namespace you should use the GalaSoft.MvvmLight.CommandWpf namespace. The RelayCommand defined in that namespace is still checking the CanExecute function that you pass to the command.

Took me about a day to find out what the hell was going wrong in my application. I hope this will help some of you.

Here is a blog post by the developer explanining why this is necessary.

Arnett answered 15/10, 2015 at 13:29 Comment(3)
Thanks! Because of your answer I didn't have to look for a day ;)Aegrotat
Even calling the CanExecute method directly wasn't helping. Changing the namespace made it works.Parsee
I did look for a day, and then found this answer! Thanks!Paulo
V
6

For some reason you have to do the following:

public RelayCommand RedoCommand{
     get;
     set;
}

you can also put private before set optional, depending on your access level. Then you do

RedoCommand = new RelayCommand(() => undoRedoController.Redo(), () => undoRedoController.CanRedo());

Now your able to call RedoCommand.RaiseCanExecuteChanged(); And everything works.

Variate answered 22/10, 2013 at 8:28 Comment(4)
I get a compiler error when trying that example. If one removes the "() =>" in the RelayCommand ctor and the parens after the method names, it works fineFluoro
Okay, can't explain that. I have the above working. If it was helpful then please up vote :)Variate
Could be the version of MVVM Light, not sureFluoro
Make sure you're using the CommandWPF namespace because the RelayCommand CanExecute is broken in the Command namespace. See blog.jsinh.in/…Fluoro
E
1

If you are using an unpatched .net 4.5. Microsoft broke the .CanExecute event.

http://connect.microsoft.com/VisualStudio/feedback/details/753666/net-4-0-application-commands-canexecute-not-updating-in-4-5

If you are using the RelayCommand from http://msdn.microsoft.com/en-us/magazine/dd419663.aspx#id0090030 and are not raising the CanExecuteChanged event when redoStack changes.

Excise answered 14/10, 2013 at 16:9 Comment(2)
I'm not raising the CanExecuteChanged event, so I have to implement the CanExecuteChanged and add it to my RelayCommand. I'm using MVVMLightBondage
Also See this: linkBarbera
H
1

(Answering from a Silverlight perspective so assuming this will help you.)

Are you doing a RedoCommand.RaiseCanExecuteChanged() anywhere? Once whatever condition you are monitoring changes, you'll need to raise this command manually.

EDIT

Since you are using MVVM Light.. Heres sample code:

RedoCommand = new RelayCommand(undoRedoController.Redo,undoRedoController.CanRedo);

   public bool CanRedo()
    {
        redoStack.Count();
        redoStack.Any();
        return redoStack.Any();
    }

    public void Redo()
    {
        if (redoStack.Count() <= 0) throw new InvalidOperationException();
        IUndoRedoCommand command = redoStack.Pop();
        undoStack.Push(command);
        command.Execute();

        // At this point, your stacks have changed; that is, the stacks
        // may or may not contain items. Thus, raise the commands CanExecute part
        // which will in turn enable/disable the commands based on the functions
        // return value

        RedoCommand.RaiseCanExecuteChanged();

        // assuming you could possibly have an UndoCommand somewhere
        UndoCommand.RaiseCanExecuteChanged();
    }
Houseboy answered 14/10, 2013 at 16:9 Comment(8)
.RaiseCanExecuteChanged() is a part of Prism's DelegateCommand library not RelayCommandExcise
Okay. Fair. But my point stands that its not a standard class, and the OP did not specify any libraries. :)Excise
@Houseboy No I'm not doing an RedoCommandRaiseCanExecuteChanged(), is this an function I have to implement myself and if so how?Bondage
@Excise I'm using MVVMLight, so I have to create an .RaiseCanExecuteChanged() myself and add this to the RelayCommand?Bondage
@Bondage You do not need to implement it. When you call RaiseCanExecuteChanged() method on your RelayCommand, your CanExecute method (CanRedo) will be invoked. You should call RaiseCanExecuteChanged() whenever you add or remove from your redo stack you are maintaining.Houseboy
@Houseboy Probably didn't make this clear, but the part where I create the RelayCommand is in a different class then where I create the voids for Redo and CanRedo. So I can't find the UndoCommand there. And is there some library I have to add to get the RaisedCanExecuteChanged() part, because so far it does not come up.Bondage
My first thought is that the class that exposes the undo/redo command should be the one to provide their implementation as well... keeping it all self contained. (sorry if im not understanding.) Can you verify that your RelayCommand is GalaSoft.MvvmLight.Command.RelayCommand? (At least that's the way the Silverlight library is - my original caveat with my answer :D)Houseboy
@Houseboy I'am using the GalasoftMVVM.Light.Command library. The problem is that if I keep it in the class where the undo/redo part is it, does not know the RedoCommand, because I assign the RedoCommand in a different class. So I've made the RedoCommand an public static, so now i'm capable of calling it from this class, but I can't call the RaiseCanExecuteChanged. The system doesn't recognize this.Bondage

© 2022 - 2024 — McMap. All rights reserved.