14

Update: The focus became MVVM instead of the actual question so I'm updating it.

I'm having a problem with CanExecute for DelegateCommand. It doesn't update before I call RaiseCanExecuteChanged, is this the desired behavior?

enter image description here

I uploaded a simple sample project reproducing this problem here : http://dl.dropbox.com/u/39657172/DelegateCommandProblem.zip

The problem is this, I have two Buttons like this. One is Binding Command to a RelayCommand implementation and the other is binding to the Prism implementation of DelegateCommand

<Button Command="{Binding DelegateSaveCommand}"/>
<Button Command="{Binding RelaySaveCommand}"/>

The ViewModel ICommands

DelegateSaveCommand = new DelegateCommand(Save, CanSaveDelegate);
RelaySaveCommand = new RelayCommand(param => Save(), param => CanSaveRelay);

and the CanExecute method/predicate

public bool CanSaveDelegate()
{
    return HasChanges;
}
public bool CanSaveRelay
{
    get { return HasChanges; }
}

Both are using the property HasChanges. When HasChanges is updated, only the CanSaveRelay updates. Is this the way it's meant to be?

Fredrik Hedblad
  • 83,499
  • 23
  • 264
  • 266
  • 2
    +1 Because I have the same issue with PRISM... I actually use MVVM Light's `RelayCommand` instead of PRISM's `DelegateCommand`. I saw an article somewhere about recompiling Prism's library to add the CanExecuteChanged event, but I can't find it now (Think it was PRISM 2 anyways) – Rachel Sep 08 '11 at 15:56
  • @Meleak: I took 2 different guesses as to what your problem could be, but without seeing more code it is impossible for me to know exactly what the cause is. – myermian Sep 08 '11 at 16:03
  • @Rachel: You might be doing the same thing as Meleak, so reference my solution for the explanation as to *why* and how to fix it. – myermian Sep 08 '11 at 16:04
  • @Meleak: Also, if the problem is that your Model is doing the property changes and raising the propertychanged events then who is actually changing the State property? – myermian Sep 08 '11 at 16:09
  • @m-y: The PropertyChanged is raised by the entity which in turn is generated from the Database. I'm reading your answer now – Fredrik Hedblad Sep 08 '11 at 16:18
  • Then you fall into the second part of my answer, and you aren't following the true MVVM pattern. In the pattern the Model should not be the one that raises the property changed. Writing and reading to and from the properties of a model should be done from the ViewModel layer (I'll show you an example in the code below). Although, if it is truly something you can not work around then you can always subscribe to the propertychanged event of your model and when the event gets raised call the RaiseCanExecuteChanged method. – myermian Sep 08 '11 at 17:22
  • @Meleak: I forgot how RelayCommand works because I haven't used it in a long time. I remember now why it is working for you in your case. Because RelayCommand subscribes to the CommandManager.RequerySuggested which is executed by the WPF UI. And, IIRC Prism was written explicitly to NOT use this technique. It was felt that the ViewModel should _ALWAYS_ know when the state of a Model changes. Basically, in your situation it's easier to stick with the RelayCommand. – myermian Sep 08 '11 at 19:46

3 Answers3

26

As it already was mentioned, this is intended behavior of DelagateCommand, not a bug. DelegateCommand doesn't raise CanExecuteChanged event automatically, you have to raise that event manually by calling RaiseCanExecuteChanged when appropriate. Whereas RelayCommand relays on CommandManager.RequerySuggested event for that. This event is raised every time the user clicks somewhere or presses a button.

For situations when it is not very convenient or there is no appropriate place for calling RaiseCanExecuteChanged (like in your scenario you have to subscribe to PropertyChanged event on the model, etc) I have created the following simple wrapper that ensures that the CanExecute method of the wrapped command is executed automatically on CommandManager.RequerySuggested event:

public class AutoCanExecuteCommandWrapper : ICommand
{
    public ICommand WrappedCommand { get; private set; }

    public AutoCanExecuteCommandWrapper(ICommand wrappedCommand)
    {
        if (wrappedCommand == null) 
        {
            throw new ArgumentNullException("wrappedCommand");
        }

        WrappedCommand = wrappedCommand;
    }

    public void Execute(object parameter)
    {
        WrappedCommand.Execute(parameter);
    }

    public bool CanExecute(object parameter)
    {
        return WrappedCommand.CanExecute(parameter);
    }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }
}

You can use it like this:

DelegateSaveCommand = new AutoCanExecuteCommandWrapper(new DelegateCommand(Save, CanSaveDelegate));
Pavlo Glazkov
  • 20,498
  • 3
  • 58
  • 71
  • 3
    Please note that this implementation depends up on `CommandManager.RequerySuggested`, which is fired only when the user clicks somewhere or presses a key (as mentioned in the answer) . If the user does not click anywhere, the command will remain inactive, this could be confusing. – Nimesh Madhavan Feb 10 '15 at 21:10
  • There is a situation where I want to use your Wrapper class as well as DelegateCommand. Because there is a need to RaiseCanExecuteCommand automatically as well as manually. After this is done automatically you would say what is the need to call it manually. There is a need to call it manually because I am changing focus to a button which is disabled. So, I want to enable the button by calling RaiseCanExecuteCommand manally. Continued in the next comment.... – Vishal Jun 03 '16 at 13:57
  • By the way I have tried to assing new AutoCanExecuteCommandWrapper(new DelegateCommand(Save, CanSaveDelegate)) to a DelegateCommand but I am getting an error that Cannot Convert type AutoCanExecuteCommand to DelegateCommand. – Vishal Jun 03 '16 at 13:57
1

If you want to stick to DelegateCommand you can use ObservesCanExecute:

DelegateSaveCommand = new DelegateCommand(Save, CanSaveDelegate).ObservesCanExecute(CanSaveDelegate);

Note that there is also ObservesProperty available if you are using a property for your CanExecute check. But then your property has to call NotifyPropertyChanged.

anhoppe
  • 4,287
  • 3
  • 46
  • 58
0

There is a bug in the DelegateCommand provided by Prism which doesn't raise the CanExecute event. I beat my head against the wall for a day until I dove into the DelegateCommand class provided by the Prism framework. I don't have the code with me, but I can post my resolution in a bit.

The alternative is to use one of the other RelayCommand frameworks out there.

Edit
Rather than reposting the code, there are other SO questions that provide resolutions:

And Kent B. has a good article: MVVM Infrastructure: DelegateCommand

Community
  • 1
  • 1
Metro Smurf
  • 37,266
  • 20
  • 108
  • 140
  • I'm actually using the `RelayCommand` implementation found in Josh Smiths MVVM article right now (and have been for a long time) and I'm very satisfied with it. Just thought I should use the `DelegateCommand` provided by Prism since I'm using Prism 4 now.. Looking forward to seeing your fix :) – Fredrik Hedblad Sep 08 '11 at 17:26
  • @Metro: No, it is not a bug. Choosing between the way to bind (prism's Click.Command or WPF Command) is the reason you must call RaiseCanExecuteChanged. It was done to give the programmer more control on when to execute a CanExecute delegate. Like I previously stated, if you want your control to **ALWAYS** query the CanExecute method, then use the `Command` property on your control. If you want to choose when the control should query the CanExecute method, then use the `Click.Command` attached property on your control. – myermian Sep 08 '11 at 17:35
  • @m-y I recall that I encountered the same behavior regardless of using the `Command` property or the attached property. It's been awhile, though. After we updated our library with a 'fix' to the default `DelegateCommand` I never looked back. – Metro Smurf Sep 08 '11 at 17:55
  • @Metro Smurf: See my updated question if you have time. I uploaded a sample project showing the problem. Is this a bug or the desired behavior? Thanks – Fredrik Hedblad Sep 08 '11 at 19:16
  • The scenario you describe in the updated question is *exactly* the same thing I beat my head against the wall with when I encountered the same behavior. Kent B's article I posted has an updated DelegateCommand (IIRC) that resolves the issue. I'll also post up the code I used to resolve the Prism `DelegateCommand` (will be a couple of hours - in the middle of some button mashing sql). – Metro Smurf Sep 08 '11 at 19:47
  • It is not a bug in the `DelagateCommand`, it is intended behavior. Also it doesn't matter how you set the command binding, via `Command` property or `Click.Command` (the latest is for Silverlight when it didn't have commanding support). – Pavlo Glazkov Sep 08 '11 at 20:09
  • @Pavlo Glazkov: So there is no difference between using `Command` or `Click.Command` in WPF? – Fredrik Hedblad Sep 08 '11 at 20:15
  • @Pavlo: Actually, it does matter. One is a property, the other is an attached property. Using the attached property asks for the queries the CanExecute method once. Afterwards it must be explicitly asked for again. The Button Command property always calls the CanExecute method when any property change is raised. – myermian Sep 08 '11 at 20:22
  • @Meleak - Right, there is no difference. `Click.Command' exists in WPF only for compatibility reasons. See the "Command Behaviors" section here for details: http://msdn.microsoft.com/en-us/library/gg405494(v=pandp.40).aspx (first paragraph). – Pavlo Glazkov Sep 08 '11 at 20:24
  • @m-y: This does not appear to be the case. I'm raising 'PropertyChanged' from the ViewModel in my sample and 'CanExecute' isn't called before I call RaiseCanExecuteChanged – Fredrik Hedblad Sep 08 '11 at 20:33
  • @m-y - See the link in my previous comment for explanation why `Click.Command` attached behavior exist. As for "The Button Command property always calls the CanExecute method when any property change is raised" - this is not true, the button call s`CanExecute` when `CanExecuteChanged` event on `ICommand` interface is raised, and when it is raised depends on particular implementation of `ICommand`. In our case it is either when `RaiseCanExecuteChanged` is called on `DelegateCommand` or when `CommandManager.RequerySuggested` is raised in case of `RelayCommand`. – Pavlo Glazkov Sep 08 '11 at 20:34
  • @Pavlo: I looked over my old code and in fact I was calling the RaiseCanExecuteChanged... I was just doing so through the EventAggregator publishing a payload to a subscriber that happened to call that method in it's implmentation... this entire time I thought it was because of the binding. Ah well. – myermian Sep 08 '11 at 21:19