12

Closed

Relay Command CanExecute() not working in WPF

description

After updating to version 5 the CanExecute() stopped working correctly. It validates the first time, like when the window is open, but it won't make any more validations after that.
Closed Oct 23, 2014 at 1:53 PM by lbugnion

comments

gorgoroth wrote Oct 11, 2014 at 9:22 AM

Yes it is broken with version 5. And my application does not work properly now.
I need a quick urgent solution because I have to deliver my application.

I'm in trouble.

lbugnion wrote Oct 11, 2014 at 12:17 PM

Hi,

Thanks for reporting. I will check it out. In the meantime, please revert to V4 until the problem is fixed.

Do you have a repro of the problem?

Thanks
Laurent

gorgoroth wrote Oct 11, 2014 at 12:29 PM

An example:

In the ViewModel a RelayCommand object is istantiated like this:

saveCommand = new RelayCommand<object>(saveCommandExecute, param => this.isValid);

isValid is a property of the ViewModel which changes according to data validation result. So if the user inserts invalid data in the view fields, the button control bound to the command will be disabled.

The lambda for CanExecute is apparently executed only the first time (during RelayCommand instantiation?) but whether isValid property changes, the lambda is not executed anymore.

lbugnion wrote Oct 11, 2014 at 1:17 PM

Hi,

In your code, do you call the RelayCommand's RaiseCanExecuteChanged method when the isValid property changes?

What version of WPF are you using?

Thanks
Laurent

gorgoroth wrote Oct 11, 2014 at 1:33 PM

No I never invoke RaiseCanExecuteChanged().

But in the previous MVVMLight version it was not necessary to call that method.

Besides, in my example, isValid is a private property used for IDataErrorInfo data validation.
If I invoke saveCommand.RaiseCanExecuteChanged() inside isValid or in any place of the IDataErrorInfo functions, a Stack Overflow exception raises. So, in this context, I do not know the exact place where I could call it...

gorgoroth wrote Oct 11, 2014 at 1:38 PM

Okay, I could invoke saveCommand.RaiseCanExecuteChanged() in each setter of each property which have to be validated and it works.

But now I have to search in every viewmodel all the RelayCommands which have an associated CanExecute and if the each command is related to data validation properties I have to modify each setter of the validating properties to add the ReaiseCanExecuteChanged() call

:(

lbugnion wrote Oct 11, 2014 at 2:00 PM

Is it a WPF 4.5 application?

I think you put the finger on a breaking change in V5. Until I find a solution to that issue, please revert to V4 where this behavior is not present. V4.4.32.7 should not have the same issue.

I am sorry about the problem. I'll try to find a way to fix it.

Cheers
Laurent

gorgoroth wrote Oct 11, 2014 at 2:01 PM

Yes, it is a WPF 4.5 application.

lbugnion wrote Oct 11, 2014 at 2:36 PM

OK so here is the explanation to the issue:

WPF is the only XAML framework that uses the CommandManager to automagically raise the CanExecuteChanged event on ICommands. I never liked that approach, because of the "magic" part, but this is a "feature" of WPF and of course I have to support it. No question here.

In V5, I moved to portable class library for all the newest versions of the XAML frameworks, including WPF4.5. Unfortunately, there is no CommandManager in PCL, and I have to admit that I didn't realize that at first sight. So of course now the automagical part doesn't work anymore. Again, so sorry about that.

I am not expecting you to raise CanExecuteChanged everywhere now, not after using the CommandManager in your application, which is what the WPF team intended. So I will try to find a way to restore the CommandManager usage in the WPF4.5 version of the toolkit.

Definitely not looking for excuses ;) but hope that explaining why the issue arose helps to make sense of this. It's going to be my priority #1 until I find a way to solve this in the PCL version. In the meantime, like I mentioned before, I think that going back to V4.4.32.7 should fix that. Please let me know if it does not.

Cheers
Laurent

gorgoroth wrote Oct 11, 2014 at 2:49 PM

No excuses needed.

Let him who writes bug free code cast the first stone :)

Xenolith wrote Oct 13, 2014 at 9:44 AM

I was about to send you the following mail:

first, i want to thank you for your great work and the magnificent work regarding MVVM Light.

However, I stumbled across something which has changed from version 4.4.7.32 to 5.0.0.0. It might be a feature (which I don't understand) but it also could be a bug.

I created a demo solution file containing two project, just change the Startup Project to see the difference.

The code is 100% identical, the only difference between both projects is the mvvmlight version.

Demoproject

jachauhan wrote Oct 19, 2014 at 11:40 PM

Hi Laurent,

Above mentioned issue is yet not fixed in version 5.0.1 as mentioned in the release notes for this version.

The issue still exist in my application where CanExecute() not working. It won't fire every time the dependent property changes (Only fires once on load).

Thanks for explaining the issue in technical terms.

bkboggy wrote Oct 20, 2014 at 5:34 AM

Having the same issue. Just updated to the latest version today and it broke every button in my application (5.0.1.0).

lbugnion wrote Oct 20, 2014 at 6:19 AM

Hi,

I worked on a fix during the weekend and will make it available to you guys in a Nuget update today. A blog post will follow. Stay tuned here for details for when the fix is available.

@jachauhan: The release notes do not mention this particular issue (7659), the issue mentioned with RelayCommand was another one.

Cheers
Laurent

lbugnion wrote Oct 20, 2014 at 7:58 PM

Guys,

I just published V5.0.2 to NuGet. It fixes the issue listed here.

There is a small catch: You need to change the namespace of the RelayCommand class from GalaSoft.MvvmLight.Command to GalaSoft.MvvmLight.CommandWpf.

The explanation of why this is needed will go into a blog post coming up soon. In short: The WPF4.5 and 4.5.1 versions of MVVM Light are now a Portable Class Library. It makes sense for many reasons. However the CommandManager does not exist in the PCL space, which is why your code, which relies on the CommandManager didn't work anymore.

Instead of just giving up and putting the RelayCommand out of the PCL (which would have sucked for everyone who wants to use RelayCommand in a PCL), I decided to have the RelayCommand both in the PCL and in the WPF4.5 platform DLLs. However, in the PCL it is in GalaSoft.MvvmLight.Command namespace and it does NOT use the CommandManager; and in the Platform DLL, it is into GalaSoft.MvvmLight.CommandWpf namespace and it DOES use the CommandManager.

Hopefully this small fix is not going to cost you guys too much time. Apologies for the bad planning. I will try to find a way to unit test the Command Manager so this kind of things does not happen again.

Cheers
Laurent

tbnguyen1407 wrote Oct 21, 2014 at 3:49 AM

I too ran into the same problem. The fix is working and modification is "minimal" (I search and replace all related usings in solution).

I don't particular like platform-specific RelayCommand but I guess we will have to stick to this arrangement for now until a new style for writing wpf commands (without relying on CommandManager's magic) appear.

Thank you anyway. My apps are working again :)

lbugnion wrote Oct 21, 2014 at 7:45 AM

Hi,

To be exact, it's not quite a platform specific implementation of RelayCommand. The problem comes from the CommandManager which is automatically used by an ICommand. I don't have a way to opt out of the CommandManager in WPF, it is there by default.

However, since it is not there in Portable Class Library, what my fix relies on is using a WPF-specific assembly (the one hosting the CommandWpf namespace) while all the others use the PCL version (in Command). The RelayCommand class is the same in both namespaces.

I know it's annoying and somewhere confusing. Honestly, I blame the WPF team from 2005 here: They wanted to make our life easier, but automagical conventions have a way to come back and bite you...

Cheers
Laurent

vunder wrote Oct 23, 2014 at 11:57 AM

Still present in 5.0.2 in WP8.0 SL projects.

I have a command RelayCommand<T> with defined Execute and CanExecute. CanExecute function is never calling. If I removing CanExecure definition, then the command is executing.

lbugnion wrote Oct 23, 2014 at 12:31 PM

Hi,

It cannot be the same issue, as that issue was only available in WPF projects. There is no CommandManager in SL.

Can you send me a repro? In SL you need to raise the CanExecuteChanged event manually.

Cheers
Laurent

vunder wrote Oct 23, 2014 at 12:34 PM

Hi
I've discovered that this is not the same issue (sorry for blaming).
Here is workaround:
  • create a generic command that receives some class
  • bind this command to XAML without value for CommandParameter
    In previous version CommandParameter without value leads to null value in code. Now command just not executing

lbugnion wrote Oct 23, 2014 at 12:43 PM

Hi,

Yes this is correct. "null" is not acceptable for the CanExecute method, and so it will simply return false. That makes sense if you think of it.

Cheers
Laurent

vunder wrote Oct 23, 2014 at 12:49 PM

But this very inconvenience. This is breaking a lot of code that had written before.
I can understand such behavior for value types but not for reference types. Why not defined CommandParameter is not NULL?

MihaMarkic wrote Jan 12, 2015 at 9:53 AM

Laurent,

I think there is a problem when CanExecute is passed as lambda in constructor. It executes a couple of times and then stops. Perhaps it has something to do with WeakReference, I am not sure.
If I instead implement an instance method it works just fine.

lbugnion wrote Jan 12, 2015 at 10:15 AM

Hi,

Please post a repro,

Thanks
Laurent

DeliaBoughton1975 wrote Jan 11, 2016 at 6:14 PM

I don't quite understand this issue. I have a WPF project (.NET 4.6). This is how it seems to work for me:

Command: Only calling RelayCommand.RaiseCanExecuteChanged works to update the UI. Calling CommandManager.InvalidateRequerySuggested() doesn't work.

CommandWpf: Calling RelayCommand.RaiseCanExecuteChanged and CommandManager.InvalidateRequerySuggested() both work to update the UI.

Is this correct? So my question is, are there any pros and cons between using RaiseCanExecuteChanged() and using InvalidateRequerySuggested()? Both methods require a method call anyway (since the CommandManager seems to be very slow/delayed to update or doesn't update automatically at all), so I don't really see any issue with using RaiseCanExecuteChanged in my code, and think it's clearer too. I just want to know if there are any advantages to using either method. I believe that InvalidateRequerySuggested() rechecks all commands — would RaiseCanExecuteChanged() perform better?

Thanks.

lbugnion wrote Jan 12, 2016 at 1:37 PM

Hi,

This issue is related to the automatic action of CommandManager in WPF. It's not about explicitly calling the method. The CommandManager will do that automatically in WPF when some UI events happen.

HTH
Laurent