maui
maui copied to clipboard
Setting 'CanExecute' doesn't change the status of Button controls when they are bound to MVVM Commands
Description
I have commands that are wired up to buttons through a view model. When the user hits "Buy AAPL", an order is generated for Apple, and the order is sent to the server. While the order is executing, I want this button disabled so they don't attempt to buy Apple stock again. To do this, I clear the 'CanExecute' flag on the command that's bound to the button. This part appears to work properly as the button is greyed out and clicking on it does nothing.
However, when the command is finished and I set the 'CanExecute' flag and call the 'CanExecuteChanged' event handler again, the status of the UI control is not updated, although the button will respond to click events again.
Oddly, the other buttons that I disabled and re-enabled during this operation are updated correctly. It's only the button bound to the command that is not updated.
Steps to Reproduce
- Build the attached solution.
- Run it.
- Hit the button.
Version with bug
6.0.400
Last version that worked well
Unknown/Other
Affected platforms
iOS, Windows
Affected platform versions
iOS15, Windows SDK 10
Did you find any workaround?
Yes, if you schedule a task to run on the main windows thread to handle the command, it appears to work.
Relevant log output
No response
From a quick debug, the button is enabled and disabled correctly, but background color changes are not notified to the handler from the VisualState.
We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process.
Thanks for the report @DRAirey1! I have two questions:
- You report the version number you're using as 6.0.400, are you on the latest Visual Studio? What is the Visual Studio version you have and what does
dotnet --version
tell you? - Could I maybe ask you to add the repro as a GitHub repo? We're unfortunately not allowed to accept zip reproductions.
Hi @DRAirey1. We have added the "s/needs-info" label to this issue, which indicates that we have an open question for you before we can take further action. This issue will be closed automatically in 7 days if we do not hear back from you by then - please feel free to re-open it if you come back to this issue after that time.
@jfversluis
Created https://github.com/Delphin-Technology/maui_9753_repo as a reproduction repository.
We are able to reproduce the issue by using CommunityToolkit.Mvvm [RelayCommand]
on a async Task
method.
Visual Studio 2022 Version is 17.3.3
dotnet --version
tells 6.0.400
I have exactly the same issue using CommunityToolkit.Mvvm
Also ran into the same issue, was very confused because at first I thought my code was wrong... Maybe it helps to let you know that, this kind of button will still work properly in an android app...
I commented my custom style and it works now....
@achilleaskar Can you explain more in detail how you solved it, ive tried all kinds of styles and methods, and i cannot get the background to update the color.
@achilleaskar Can you explain more in detail how you solved it, ive tried all kinds of styles and methods, and i cannot get the background to update the color.
I just didn't use the custom style... Left the default. I spent many hours trying to fix this and it didn't worth it. I haven't tried if it works in the .Net 7 version tho, maybe I will try it today and feedback here
I have a similar issue with my buttons bound to commands with CanExecute functionality. My buttons have various stylings for background colors. When compiled for Debug, everything works as expected. When compiled for Release, the buttons' BackgroundColor do not update. However, the buttons' IsEnabled states update.
So, things technically work but the users are going to be really confused if I release the app like this. I'd rather not have to go through the application and overlay a second disabled button on top of all my existing buttons but I might have to.
Quick update... For sh*ts and giggles, I ran the Release build in a "Windows Machine" and the buttons behaved normally. So, this is just an issue with Android. (I have no idea how iOS behaves.) Also, this issue persists in both my VS emulator and on a physical device where the .APK has been sideloaded.
I do use the same RelayCommand with CanExecute method and C# Markup instead of xaml.
Button is clickable as expected but colors does not change.
It does not work on both debug and release builds on Windows Machine.
Also confirmed same behavior. On a windows machine the button works, but shows disabled. If you right click on the button, the button will appear enabled.
Visual Studio 17.3.5 dotnet --version 6.0.401
So looking into this it seems that this indeed is a visual thing. The button is in fact enabled, but it doesn't show visually.
However, on the latest version of .NET 6 and now on .NET 7, it seems to work (with the reproductions here) on iOS/macOS and Android, but not yet on Windows.
Does that seem correct?
However, on the latest version of .NET 6 and now on .NET 7, it seems to work (with the reproductions here) on iOS/macOS and Android, but not yet on Windows. Does that seem correct?
No. Just the opposite. It works just fine in Windows. It even seems to work on the iOS simulators, but it fails when you put the actual code on the device (either through the App Store or Ad Hoc loading).
That's very interesting, because your example is only not working on Windows for me 😄 but the physical devices angle is interesting to. Need to do some testing then.
But just to clarify, you did upgrade to newer versions and you still see this?
Upgrade to .net 7 did not solve it for me.
In MAUI I have the RelayCommands working now if I add a parameter to the command which is enabling the CanExecute to be refreshed. i.e I needed to have a commandParameter even though its not actually used in my command.
<Button x:Name="btnDelete"
Content="Delete"
Command="{Binding AddBatchCommand}"
CommandParameter="{Binding SelectedProduct}"
</Button>
Heyho, I ran into this issue when using the CommunityToolkit.Mvvm RelayCommand without setting a CanExecute, tested on Windows.
When subscribing to IsEnabledChanged I found that executing the command quickly toggles IsEnabled from true -> false -> true but the button keeps the disabled state. I also navigate with the (async) command so this might add to the problem.
I suspect that this override of the visual state setting might cause the issue: https://github.com/dotnet/maui/blob/2921206fcbe460cba019f921c00b9ae9103e88db/src/Controls/src/Core/Button.cs#L365
I could solve it by resetting the VisualState of the button.
#if WINDOWS
Microsoft.UI.Xaml.Controls.Button platformView = handler.PlatformView;
Microsoft.Maui.Controls.Button button = (Button)handler.VirtualView;
platformView.Loaded += (s, e) =>
{
if (button.IsEnabled && button.IsPressed)
{
VisualStateManager.GoToState(button, VisualStateManager.CommonStates.Normal);
}
};
#endif
That's very interesting, because your example is only not working on Windows for me 😄 but the physical devices angle is interesting to. Need to do some testing then.
I'm in the same boat as @jfversluis - the original repro project from @DRAirey1 works fine on Android (on a device) and fine on iOS (both on the simulator and on a physical device, built in Release mode). On Windows, the button remains functional, but the style is wrong (it stays in the disabled style).
Debugging this on Windows, I'm seeing the GoToState method called for the Button several extra times. I wonder if the extra state transitions are breaking the transition out of the Disabled state back to Normal.
On Android, the state transition is only called once to go to Disabled, and once to go back to Normal.
Update: the extra calls are happening because of pointer events. So @krdmllr's theory about the override in Button is looking promising.
Okay, I've got a branch here that seems to fix the problem for Windows; it's not well tested yet.
Okay, we've got a fix in for the Windows version of this problem. We've had no luck reproducing the issue on physical devices or simulators using the original reproduction project and the latest stable versions of VS/MAUI, so this has probably been fixed for those platforms in another PR. I'm going to add the "try latest version" tag on this to start the countdown to closing it.
If anyone is still hitting this issue, reply here and we can stop the countdown/reopen the issue.
Hi @DRAirey1. We have added the "s/try-latest-version" label to this issue, which indicates that we'd like you to try and reproduce this issue on the latest available public version. This can happen because we think that this issue was fixed in a version that has just been released, or the information provided by you indicates that you might be working with an older version.
You can install the latest version by installing the latest Visual Studio (Preview) with the .NET MAUI workload installed. If the issue still persists, please let us know with any additional details and ideally a reproduction project provided through a GitHub repository.
This issue will be closed automatically in 7 days if we do not hear back from you by then - please feel free to re-open it if you come back to this issue after that time.
I'll try and swing back around to this later in the week and see if I'm still hitting this issue as well. Thanks!
@acrigney suggestion solved it for me on .net 7. Without it didn't update the button either.
In MAUI I have the RelayCommands working now if I add a parameter to the command which is enabling the CanExecute to be refreshed. i.e I needed to have a commandParameter even though its not actually used in my command.
<Button x:Name="btnDelete" Content="Delete" Command="{Binding AddBatchCommand}" CommandParameter="{Binding SelectedProduct}" </Button>
Confirmed. 7.0 fixes the issue. Buttons now behave as expected.
I just created a new MAUI app with latest Visual Studio 17.4.0
I'm still able to reproduce this issue on net7.0-windows10.0.19041.0 with simple repro below. Is this expected to be fixed in the version that I have, or is there a workaround? Thanks.
<ContentPage.Resources>
<local:ReproObject x:Key="MyReproObject" />
</ContentPage.Resources>
<StackLayout BindingContext="{DynamicResource MyReproObject}">
<Label Text="{Binding Count}" />
<Button Text="Increment"
Command="{Binding IncrementCommand}" />
<Button Text="Decrement on Even"
Command="{Binding DecrementOnEvenCommand}" />
</StackLayout>
public class ReproObject : INotifyPropertyChanged
{
private int count = 0;
public int Count => count;
private ICommand _increment;
public ICommand IncrementCommand => _increment ??= new Command(execute:
() =>
{
count++;
RefreshCanExecutes();
RaiseCountChanged();
}, canExecute: () => true);
private ICommand _decrementOnEvenCommand;
public ICommand DecrementOnEvenCommand => _decrementOnEvenCommand ??= new Command(execute:
() =>
{
count--;
RefreshCanExecutes();
RaiseCountChanged();
}, canExecute: () => count % 2 == 0);
private void RefreshCanExecutes()
{
(IncrementCommand as Command)?.ChangeCanExecute();
(DecrementOnEvenCommand as Command)?.ChangeCanExecute();
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaiseCountChanged()
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Count)));
}
}