Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

After updating Mvvm Light to Version 5, what changes do I need to make RelayCommand CanExecute() work?

Tags:

c#

wpf

mvvm-light

I updated Mvvm Light to version 5 and noticed that RelayCommand stopped working.

The problem seems to be that the CanExecute() is not being called to validate. It only validates one time, like when the window is loaded.

Could this be a bug from the recent update or is there something I need to change in the XAML?

Everything was working correctly before the update. I'm working with WPF.

like image 351
Andres Ramos Avatar asked Oct 10 '14 19:10

Andres Ramos


2 Answers

See this MVVM Light 5 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 No. 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.

So the temporary recommended solution is to revert back to the previous version. I did it and it worked.

I agree that the CommandManager is doing "magic". Once I had null reference exception within the CanExecute condition and as a result I have obtained neverending cycle of error messages like cards in Windows Solitaire. If I started a new project I would prefer not to use this "magic" but to change an existing and already deployed project would be quite painful.

like image 94
Vojtěch Dohnal Avatar answered Nov 09 '22 23:11

Vojtěch Dohnal


Looks like you have to call RaiseCanExecuteChanged on the command whenever a relevant property is changed. I think this was previously done by the wpf command manager, but the recent PCL changes probably made that unsupportable.

I use MvvmCross which requires a similar method to be called, so in my base view model I have a method allowing me to register a command so that whenever a property changed occurs I can loop all registered commands and force canexecute to be called again. To make things easier you could do it that way.

Check out this sample

As this seems to be doubted, here is some code I have tested

using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using System;

namespace TestingApp.ViewModel
{
    /// <summary>
    /// This class contains properties that the main View can data bind to.
    /// <para>
    /// Use the <strong>mvvminpc</strong> snippet to add bindable properties to this ViewModel.
    /// </para>
    /// <para>
    /// You can also use Blend to data bind with the tool's support.
    /// </para>
    /// <para>
    /// See http://www.galasoft.ch/mvvm
    /// </para>
    /// </summary>
    public class MainViewModel : ViewModelBase
    {
        private Int32 _increment = 0;

        public Int32 Increment
        {
            get { return _increment; }
            set
            {
                _increment = value;
                RaisePropertyChanged("Increment");
                GoCommand.RaiseCanExecuteChanged();
            }
        }

        /// <summary>
        /// Initializes a new instance of the MainViewModel class.
        /// </summary>
        public MainViewModel()
        {
            ////if (IsInDesignMode)
            ////{
            ////    // Code runs in Blend --> create design time data.
            ////}
            ////else
            ////{
            ////    // Code runs "for real"
            ////}
        }

        private RelayCommand _incrementCommand;

        public RelayCommand IncrementCommand
        {
            get
            {
                if (_incrementCommand == null)
                {
                    _incrementCommand = new RelayCommand(IncrementCommand_Execute);
                }
                return _incrementCommand;
            }
        }

        private void IncrementCommand_Execute()
        {
            Increment++;
        }

        private RelayCommand _goCommand;

        public RelayCommand GoCommand
        {
            get
            {
                if (_goCommand == null)
                {
                    _goCommand = new RelayCommand(GoCommand_Execute, GoCommand_CanExecute);
                }
                return _goCommand;
            }
        }

        private bool GoCommand_CanExecute()
        {
            return Increment > 5;
        }

        private void GoCommand_Execute()
        {
            //
        }
    }
}

Without the line

GoCommand.RaiseCanExecuteChanged();

the canexecute method is never called after the initial call, but with the line it is called everytime the Increment property changes

like image 45
ndonohoe Avatar answered Nov 10 '22 00:11

ndonohoe