Mvvmcross: WPF enable/disable button on MvxCommand can execute criteria change

Created on 25 Jun 2017  路  8Comments  路  Source: MvvmCross/MvvmCross

I want to update UI button (enable/disable) depending on some other conditions.
On version 4, it was enough to call RaiseCanExecuteChanged() on a command, but this has stopped working on version 5.

Here is a simple project on GitHub showing the issue

Steps to reproduce

  1. Create a solution with Core and Pc projects, add MvvmCross nuget packages.

  2. Update MainViewModel with following content:

        public bool AllowExecution
        {
            get { return _allowExecution; }
            set
            {
                SetProperty(ref _allowExecution, value);
                DoWorkCommand.RaiseCanExecuteChanged();
                DoAsyncWorkCommand.RaiseCanExecuteChanged();
            }
        }

        public IMvxCommand DoWorkCommand =>
            new MvxCommand(DoWork, () => AllowExecution);
        private void DoWork()
        {
            // Work logic here
        }

        public IMvxAsyncCommand DoAsyncWorkCommand =>
            new MvxAsyncCommand(DoAsyncWorkAsync, () => AllowExecution);
        private Task DoAsyncWorkAsync()
        {
            // Async work logic here
            return Task.FromResult(true);
        }
  1. Update MainView with following XAML:
<Grid>
    <StackPanel>
        <CheckBox IsChecked="{Binding AllowExecution}">Allow Execution</CheckBox>
        <Button Content="Do Work" Command="{Binding DoWorkCommand }" />
        <Button Content="Do Async Work" Command="{Binding DoAsyncWorkCommand }" />
    </StackPanel>
</Grid>

Expected behavior

Button should get enabled when checkbox is checked, and disabled otherwise.

Actual behavior

Button never gets updated.

Depending on initial status of checkbox, button is enabled or disabled.

Configuration

Windows 10
VS 2017

Version: 5.0.4

Platform:

  • [ ] iOS
  • [ ] Android
  • [X] WPF
  • [ ] UWP
wpf bug up-for-grabs

Most helpful comment

I have this problem on UWP. Calling RaiseCanExecuteChanged() doesn't result in CanExecuteChanged being called. This only works initially (shortly after creating the command) and I guess then the reference is disposed. This seems to be an issue with MvxWeakCommandHelper.

When using the MvxStrongCommandHelper instead it works without a problem.

Mvx.RegisterType<IMvxCommandHelper, MvxStrongCommandHelper>();

All 8 comments

Thank you for the report. I will take a look and see if I can reproduce and narrow down what exactly the issue is.

If you set a breakpoint in your CanExecute action, is it triggered when you call CanExecuteChanged?

No it is not triggered.
CanExecute is being called only once, when the application starts, and never again.

It seems that this issue is related to CommandManager.
Have a look at this post, and it's source code.

From the code above, I have created a custom Command with WpfCommandHelper that works.
Working source code is in NET46 branch, but this needs to be converted to be used with MvxCommand

As a workaround, registered following at DoSetup

Mvx.RegisterType<IMvxCommandHelper, MvxWpfCommandHelper>();

simple command helper code

    public class MvxWpfCommandHelper : IMvxCommandHelper
    {
        public event EventHandler CanExecuteChanged
        {
            add
            {
                CommandManager.RequerySuggested += value;
            }

            remove
            {
                CommandManager.RequerySuggested -= value;
            }
        }

        public void RaiseCanExecuteChanged(object sender)
        {
            CommandManager.InvalidateRequerySuggested();
        }
    }

I have this problem on UWP. Calling RaiseCanExecuteChanged() doesn't result in CanExecuteChanged being called. This only works initially (shortly after creating the command) and I guess then the reference is disposed. This seems to be an issue with MvxWeakCommandHelper.

When using the MvxStrongCommandHelper instead it works without a problem.

Mvx.RegisterType<IMvxCommandHelper, MvxStrongCommandHelper>();

Thanks, @flostr. Working like a charm.

To me it looks like there is a problem in the view model code. There is a new instance of the command created on every "get". The instance that RaiseCanExecuteChanged is called on is not the same instance as the one the the bound control has. Same as #2102

I encountered the problem with the weak referencing being disposed in the MvxAsyncCommand before it should have, specifically the IsAlive property/field being set to false. In my case the Command object was created in the constructor of the ViewModel.

By registering the MvxStrongCommandHelper resolved the issue for me. However, I feel this is a work around and not an ideal fix.

I'm using MvvmCross 6.4.1 and Xamarin.Forms 4.3.0.947036.

Was this page helpful?
0 / 5 - 0 ratings