maui
maui copied to clipboard
Button IsEnabled property in Windows Platform
Description
When I set up the Button's "IsEnabled" property, programmatically. The expected behavior is working in all platforms. However, the formatting of the button in the Windows Platform gets lost after a couple of changes. In the Android platform it works as expected.
Steps to Reproduce
- Create a new Maui App
- Add a Grid and a StackLayout with two buttons
- Disable of the buttons by setting IsEnabled="False"
- Add code to programmatically change the is enabled property.
<Grid BackgroundColor="Black">
<StackLayout Orientation="Horizontal" HorizontalOptions="Center">
<Button x:Name="btn1" Text="Start" WidthRequest="100" Background="Green" Clicked="btn1_Clicked"></Button>
<Button x:Name="btn2" Text="Stop" WidthRequest="100" Background="Green" Clicked="btn2_Clicked" IsEnabled="False"></Button>
</StackLayout>
</Grid>
private void btn1_Clicked(object sender, EventArgs e)
{
btn1.IsEnabled = false;
btn2.IsEnabled = true;
}
private void btn2_Clicked(object sender, EventArgs e)
{
btn1.IsEnabled = true;
btn2.IsEnabled = false;
}
5.Click on the buttons a couple of times to enable and disable them. After the first cycle both buttons will look disabled. the functionality will still be working but it is confusing for the user. This only happens in windows, not in Android or IOS.
Version with bug
Release Candidate 3 (current)
Last version that worked well
Release Candidate 3 (current)
Affected platforms
Windows
Affected platform versions
Windows 11
Did you find any workaround?
No
Relevant log output
No response
verified repro on windows. repro project: MauiApp36.zip
I am also experiencing this same issue on Windows builds. This appears to be happening on Windows 10 in addition to Windows 11 (as reported by @jcmontoya).
this also never really worked well in XF, so there I used VisualStateManager
to change the IsEnabled
property and here the change always worked. But in MAUI VisualStateManager
is broken (#8003), so I can't use it.
Has anyone found a working way to change button states correctly?
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.
Need fix the issue asap, it also affects Android platform
When is this issue going to be addressed? This issue is now 5 months, is a major piece of broken functionality on Windows, no known workaround, no official response, and sits unassigned and with no milestone. For all practical purposes this is looks like a WNF, the silence is speaking volumes.
Could you try again with the latest version (net7 rc2)?. Cannot reproduce the issue.
Hi @jcmontoya. 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.
@jsuarezruiz Does your last comment imply that this issue will NOT ever be addressed in net6?
Seems the issue happens if the button is re enabled after an await method.
I'm attaching a screenshot of my code snippet for reference.
The IsEnabled = True
works as intended if there was no await call between setting it false
and true
. But if there is an await call before resetting it to true, the visual element does not reset.
Image Reference
I also have the same issue with .NET 6, attaching a screenshot.
_grid.Children
is just a typed version of the Grid.Children
, I use _grid.Children.Select(x => (Button)x)
.
IsVisible
works though.
I encountered the same issue with .NET 7. A repo for reproduction: ButtonIssueSampleNet7.zip
Capture: https://user-images.githubusercontent.com/25539067/200715698-6048860a-cd40-4849-a77c-2dab88de872d.mp4
I tried fiddling around to find a (somewhat stupid) workaround, as in actually having two buttons, one permanently enabled and one permanently not, and binding both their IsVisible properties to the same boolean with an inverse converter.
The correct buttons are displayed, yet the button with the enabled status fixed at "True" STILL grays itself out when it appears. It seems something is forcing the IsEnabled property to false, perhaps with these await statements as mentioned by @NirabhraDas013.
Not quite to be honest. The button itself is not disabled. The click function works as intended but the visuals are stuck at disabled status.
Edit : I meant "it seems something is forcing the BackgroundColor property to match an "IsEnabled = false" state. The button IsEnabled status in itself is working correctly."
The (very ugly) workaround that does work however is to make the button color transparent and place a boxview right behind your button, with a binded Color property. If it's stupid but it works...
So writing a custom control "CustomButton" composed of a boxview and a button could be a somewhat decent workaround ?
I can confirm the same issue ~~and the analysis of await
vs not seems to be spot on.~~
@5CoJL --
It seems something is forcing the
IsEnabled
property to false
Not quite to be honest. The button itself is not disabled. The click function works as intended but the visuals are stuck at disabled status.
The (very ugly) workaround that does work however is to make the button color transparent and place a
BoxView
right behind your button, with abinded
Color property. If it's stupid but it works...
I ended up doing something similar. but maybe a bit more complicated than it needed to be (being new to . NET doesn't help haha). I positioned a Label
with a border to set it look like the button and turned it on and fixed Text
and grayed background. This wat the Button
is never disabled just hidden from view
@borrrden --
I'm not a .NET developer by trade. I'm usually Unity Developer. I've tried my hands at Avalonia and now trying MAUI as we need a cross platform desktop app. I might have missed something in the await
analysis. But I have tried multiple times with new projects and the behavior before and after the await
call remains same for me. Could you share your findings please so I can try to look from a different angle
@Catherine25 --
Have you checked if the button click works after the IsEnabled = true
call? For me only the visual was stuck at the disabled state but the button click still worked so the button itself had been enabled
@NirabhraDas013,
I just double checked. For me the click works only when another button was pressed after mine. Clicking anything else and minimizing the window will not make the same effect.
So, 2+ times clicking on A
button will still raise Click
event only 1 time.
When I click for example A
-> B
-> A
, Click
event might be raised 2+ times.
@NirabhraDas013 I take it back, It's not completely that cut and dry. Here is the situation in my application:
I have two buttons (say, A and B) and an Entry field. At the start A is enabled, and B is disabled. They are bound to the same backing variable, just one is run through an InvertedBoolConverter
.
- Pushing A sets the backing variable to true, which sets A to disabled and B to enabled, and then performs an
await
. The visual states are as expected (A gray B blue) - Changing the text of the entry field resets the backing variable to false, which sets A to enabled and B to disabled. This results in both buttons being in the disabled visual state (gray) which is wrong.
- Repeating 1 puts the buttons back into the correct state (A gray B blue)
I use VisualStateManager to change the change the state via VisualStateManager.GoToState(ButtonLog1l, "Disabled");
. My mistake was to not include <VisualStateGroupList>
entry now everything works.
<Button x:Name="Button1" Text = "Foo" Clicked="OnButton1Clicked">
<VisualStateManager.VisualStateGroups>
<VisualStateGroupList>
<VisualStateGroup Name="ValidityStates">
<VisualState Name="Enabled" />
<VisualState Name="Disabled">
<VisualState.Setters>
<Setter Property="IsEnabled" Value="False" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</VisualStateManager.VisualStateGroups>
</Button>
IsEnabled works for me if I never ever modify it synchronously from a Clicked event.
Just awaiting anything does not guarantee that the rest of the method runs asynchronously. I must await Task.Yield to be sure.
This is only necessary on Windows. When running on Android, IsEnabled works as expected.
private async void OnMyButtonClicked(object sender, EventArgs e)
{
await Task.Yield(); // without this line all future calls (in all methods) to buttonDisconnect.IsEnabled will not work
buttonDisconnect.IsEnabled = false;
// the rest of your code here
}
@tomas-andersson await Task.Yield()
works for me! Thank you very much!
Did anyone find a workaround for commands?
[RelayCommand(CanExecute = nameof(CanExecute))]
private async Task Second()
{
await Task.Yield();
CanExecute = false;
await Task.Delay(3000);
CanExecute = true;
}
Is still not working for me. The binded button is stuck in the greyed-out state, even though it becomes clickable after the 3s delay.
Today updated my project to .net 7 (minimum target windows framework 10.0.22000.0) Issue is still there.
Today updated my project to .net 7 (minimum target windows framework 10.0.22000.0) Issue is still there.
use VisualStateManager, this works fine for me and also worked in XF, but in MAUI compared toXF I need to add <VisualStateGroupList>
I really don't want to be writing all the boilerplate that comes with visual states just to convey an enabled/disabled button.
I found a workaround: using the command interface. https://learn.microsoft.com/en-us/dotnet/maui/user-interface/controls/button?view=net-maui-7.0#use-the-command-interface Change button's state via function "canExecute", works for me.
I really don't want to be writing all the boilerplate that comes with visual states just to convey an enabled/disabled button.
in XF I also sometime got issues enabling/disabling buttons and here VisualStateManager
always worked. So you should use the proper solution VisualStateManager
Morning guys, any update on this? Still happens on VS 2022 17.4.2 and .NET 7.0.100
If it helps, I noticed a weird addition to this issue. Firstly, yes the issue seems to only repro if an async method that actually does something is called (for example, await DisplayAlert(...)). So, for example, we start with IsEnabled on the button true:
if IsEnabled is set false then the button shows disabled. Then the DisplayAlert is called. You will see that the button is, as expected, disabled.
We click 'OK' on the alert, then IsEnabled is set true. Now it stays in the visual state for disabled, even though it is actually enabled as it is clickable again:
The weird addition is that if I simply copy-paste to put TWO buttons on the page, then the one I don't click works as expected. However, the one I clicked stays in the disabled state. See example below. Firstly, before we click the first button:
Next we click the first button. Note that BOTH buttons disable, as they should (I am binding to an enabled property):
We click 'OK' and the alert closes... but the button I clicked stays disabled. The one I did NOT click shows the enabled visual state though!
It appears that it's something to do with the actual click handling on windows somehow overriding the visual state callback once it's done, or something related to that.
The initially reported issue and the version of the issue with async/await are fixed by #11840. The fix should be in the next service release.