4

I just tried to update one of my WPF projects from MVVM Light 4.2.30 to 5.2. After that I noticed that my RelayCommands do not fire their CanExecute methods anymore.

After a quick search I found several articles that explain the problem and suggest using the GalaSoft.MvvmLight.CommandWpf namespace instead of GalaSoft.MvvmLight.Command. However I cannot find the GalaSoft.MvvmLight.CommandWpf namespace. When I look at the GalaSoft.MvvMGalaSoft.MvvmLight.dll in Visual Studio's 'Object Browser' then I also cannot find this namespace.

As it seems nobody else but I have that problem - any ideas what I'm doing wrong?

Update:

I've created a small example project that shows how I currently use the RelayCommands with their CanExecute methods in Version 4.2.30 of MVVM light:

public class ViewModel : ViewModelBase
{
    private bool _isReadOnly = false;

    public ViewModel ()
    {
        this.DoSomethingCommand = new RelayCommand(DoSomething, CanDoSomething);
    }

    public bool IsReadOnly
    {
        get
        {
            return _isReadOnly;
        }

        set
        {
            _isReadOnly = value;
            this.RaisePropertyChanged("IsReadOnly");

            // With MVVMLight 4.2.30.23246 I did not need to call the RaiseCanExecuteChanged on any of my RelayCommands
            // DoSomethingCommand.RaiseCanExecuteChanged(); 
        }
    }

    public RelayCommand DoSomethingCommand { get; set; }

    private bool  CanDoSomething()
    {
        return !this.IsReadOnly;
    }

    private void DoSomething()
    {
        MessageBox.Show("Let's break the MVVM idea...");
    }
}

The XAML code of the view is:

<Window x:Class="MVVMLight5.2CanExecuteTest.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:MVVMLight5._2CanExecuteTest"
    mc:Ignorable="d"
    Title="Test" Height="150" Width="200">
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <CheckBox HorizontalAlignment="Center" Grid.Row="0" Grid.Column="0" Content="Is read only" IsChecked="{Binding IsReadOnly, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
    <Button Grid.Row="1" Grid.Column="0" Content="Break me" Command="{Binding DoSomethingCommand}"/>
</Grid>

My target is that if I have a button in the View that uses the 'DoSomethingCommand' as a Command then this button should become disabled when my IsReadOnly property is turned to false. When using MVVM light 4.2.30 then this works without any additional so far but in MVVM light 5.2 I need to add the DoSomethingCommand.RaiseCanExecuteChanged(); to make the button go disabled in view.

Can I somehow get the old behavior with the new MVVM light framework?

Patrick Quirk
  • 23,334
  • 2
  • 57
  • 88
Ralf
  • 293
  • 5
  • 15
  • The `RelayCommand` class is in the `CommandWpf` namespace only if you are referencing the PCL (portable) version of the library. When targeting .NET 4.0 and below, it's in the `Command` namespace. Are you sure it's MvvmLight at fault? Can you post any code? – Patrick Quirk Nov 10 '15 at 13:42
  • No I'm not using the portable version - I'm referencing the regular 4.5 version of the library. Why is this only available in the PCL version and only for .NET 4.0? How can I fix my problem then? – Ralf Nov 10 '15 at 15:39
  • No answer found so far. In the meantime I wonder how the CanExecute methods are meant to be called in the current versions of MVVM light: Manually? Maybe within a timer? – Ralf Nov 16 '15 at 16:11
  • Can you post the code you use to bind to the command, as well as the code to create the command and the `CanExecute` method itself? I don't have any issues with it and I don't know of any current bugs with `CanExecute` not being called. Either that or create a [SSCCE](http://sscce.org/) that exhibits the bug. – Patrick Quirk Nov 16 '15 at 16:50
  • Hi Patrick! Sorry for the later answer.... I've updated my post. – Ralf Nov 18 '15 at 14:57

1 Answers1

4

TL;DR: Ensure you have a reference to GalaSoft.MvvmLight.Platform, and then you can use the CommandWpf namespace. Or, double check that you're linking against the .NET 4.5 version of MVVM Light; I think you're linking to the .NET 4.0 version

Laurent (the author of MVVMLight) has actually addressed all of this on his blog. On why he created a different namespace:

In the portable class library version of MVVM Light, there is no CommandManager.

...

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.

With that context, I'm still a little confused as to why things are the way they are. When I disassemble the libraries published on Nuget, here's what I see:

MvvmLightLibs.5.2.0.0\lib\net40 (assembly version 5.2.0.37222):

namespace GalaSoft.MvvmLight.Command
{
    /// <omitted, see below>
    public class RelayCommand<T> : ICommand
    {
        private readonly WeakAction<T> _execute;
        private readonly WeakFunc<T, bool> _canExecute;

        /// <summary>
        /// Occurs when changes occur that affect whether the command should execute.
        /// 
        /// </summary>
        public event EventHandler CanExecuteChanged;

MvvmLightLibs.5.2.0.0\lib\net45 (assembly version 5.2.0.37223):

namespace GalaSoft.MvvmLight.Command
{
    /// <omitted, see below>
    public class RelayCommand<T> : ICommand
    {
        private readonly WeakAction<T> _execute;
        private readonly WeakFunc<T, bool> _canExecute;

        /// <summary>
        /// Occurs when changes occur that affect whether the command should execute.
        /// 
        /// </summary>
        public event EventHandler CanExecuteChanged
        {
            add
            {
                if (this._canExecute == null)
                    return;
                CommandManager.RequerySuggested += value;
            }
            remove
            {
                if (this._canExecute == null)
                    return;
                CommandManager.RequerySuggested -= value;
            }
        }

Note that the 4.5 version of CanExecuteChanged uses the CommandManager class which is why you never needed to call RaiseCanExecuteChanged(). The 4.0 version is just a regular event, so you need to call RaiseCanExecuteChanged() yourself.

Also note the assembly versions are different between the two, and since in your question you say you're using 5.2.0.37222, I'd say you're using the 4.0 library. This is why I think just referencing the 4.5 version will fix it.

Unfortunately I can't figure out why the two versions are different by looking at the source code. None of those three constants are defined in the .NET 4.0 version of the project, so why doesn't it generate the branch with the CommandManager?

Community
  • 1
  • 1
Patrick Quirk
  • 23,334
  • 2
  • 57
  • 88
  • Adding the GalaSoft.MvvmLight.Platform.dll as a reference allowed me to use the GalaSoft.MvvmLight.CommandWpf namespace and thus solved my problem. I have to admit that I must have been somehow stupid to not understand that from the beginning (I've also read Laurent's post) before starting this thread here. Thanks for the help and sorry for the trouble... :-/ – Ralf Nov 20 '15 at 13:48
  • I only got more confused the deeper I went, so it's completely understandable! – Patrick Quirk Nov 20 '15 at 13:49