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
Create a solution with Core and Pc projects, add MvvmCross nuget packages.
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);
}
<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>
Button should get enabled when checkbox is checked, and disabled otherwise.
Button never gets updated.
Depending on initial status of checkbox, button is enabled or disabled.
Windows 10
VS 2017
Version: 5.0.4
Platform:
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.
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 withMvxWeakCommandHelper.When using the
MvxStrongCommandHelperinstead it works without a problem.