Re-enabling the CommandManager feature with RelayCommand in MVVM Light V5
In October, I published V5 of the MVVM Light Toolkit. Amongst other things, this is the first version where the main assemblies (GalaSoft.MvvmLight and GalaSoft.MvvmLight.Extras) are portable class libraries (PCL). This is good for you because you can now develop PCLs that reference MVVM Light and use its components. It’s good for me too, because it creates less clutter when I want to support multiple Windows frameworks (and boy, do I support them! All XAML frameworks are officially supported by MVVM Light V5 as well as Xamarin.iOS, Xamarin.Android and Xamarin.Forms!)
For you who are using MVVM Light as your framework of choice in an application, it should mean much change. In WPF 4 and WPF 4.5, however, there is a catch: The CommandManager will stop working after you upgrade MVVM Light to V5. What you will observe is that your UI elements (buttons, etc) will stop getting disabled/enabled when the RelayCommand’s CanExecute delegate returns false.
TLDR; (too long, didn’t read)
If you are in a hurry, here is the fix: In any class that uses the RelayCommand, replace the line saying:
using GalaSoft.MvvmLight.Command;
with:
using GalaSoft.MvvmLight.CommandWpf;
So what is the CommandManager?
The CommandManager in WPF probably sounded like a great idea when the Avalon team came up with it. In practice however, it’s not that great…
This object is managing the commands (yes!). Notably, it is managing the enabling and disabling of UI elements that are using a Command. In practice, these are elements deriving from ButtonBase, i.e. any element that has a Command property. This property can be databound to a property of type ICommand.
The ICommand interface specifies two methods and an event:
- The Execute(object) method
- The CanExecute(object) method
- The CanExecuteChanged event
The idea is that any consumer of an implementation of this interface can ask the ICommand if it can be executed in the current conditions, by calling the CanExecute method that returns a bool. If the conditions change, and the CanExecute method should be re-evaluated, the ICommand instance should raise the CanExecuteChanged event, which instructs the consumer to call CanExecute again.
In theory this is simple enough. In practice, the WPF architects thought it would be nice to have an object that manages that automatically, and re-evaluates all the commands’ CanExecute methods every time that something happens on the UI. This could be a user click, an object getting or losing the focus, a window being brought to front, etc. This, you guessed it, is the CommandManager’s task.
I am not a fan of the CommandManager, because it is too magic. I like to see what is happening, and why some elements are enabled/disabled. This has always been a principle in MVVM Light, and why I prefer configuration over conventions. But in WPF, it is here and many people are relying on it.
Why does it stop working? How did you fit it?
In the portable class library version of MVVM Light, there is no CommandManager. Remember that PCL is a subset of the full .NET, and this subset can be larger or smaller depending on the number of frameworks that you support. When you support WPF and other frameworks like Windows Phone or Windows Store, there is no CommandManager. What it means is that suddenly, the magic stops working, and the UI elements are not enabled/disabled automatically anymore.
In order to solve this, I had a difficult choice to make. The first obvious solution I considered was this: Move the RelayCommand out of the GalaSoft.MvvmLight.dll assembly and into the GalaSoft.MvvmLight.Platform.dll. Because this DLL is not a PCL (hence the “platform” name), it can contains platform-specific elements. If I move the RelayCommand there, it will work with the CommandManager just like before. However, it will not be available for PCL-based assemblies anymore, which is a real issue. That’s why I decided against this decision.
Instead, I decided to duplicate the RelayCommand class. If you check the WPF source code, you will see that this class is linked in the GalaSoft.MvvmLight assembly, and also in the GalaSoft.MvvmLight.Platform assembly. This is the same class! To avoid conflicts however, the one in the Platform assembly is declared in the GalaSoft.MvvmLight.CommandWpf namespace.
Am I fully satisfied with this solution?
Short answer: no. It’s kind of a dirty hack, and it’s not usually my style. But I have to say, given the circumstances, I didn’t really have a choice. The alternative would be to not have RelayCommand in the PCL assembly, which would be bad, or to exclude WPF from the PCL altogether, which would kind of defeat the purpose of using a PCL in the first place. Because of this, I feel that this is the “least bad” solution to this problem.
Hopefully this helps to understand why the change was made, and how to restore the initial functionality. Note that if your application is using MVVM Light V4, or if you are using WPF 3.5 or WPF 4, you don’t have to worry about this issue.
Happy coding
Laurent