Skip to content
This repository has been archived by the owner on May 1, 2024. It is now read-only.

The order of 'IsEnabled' and 'Command' should not matter #2758

Open
mattleibow opened this issue May 17, 2018 · 24 comments
Open

The order of 'IsEnabled' and 'Command' should not matter #2758

mattleibow opened this issue May 17, 2018 · 24 comments
Labels
breaking Changes behavior or appearance e/5 🕔 5 help wanted We welcome community contributions to any issue, but these might be a good place to start! m/high impact ⬛ p/0 t/bug 🐛 up-for-grabs We welcome community contributions to any issue, but these might be a good place to start!

Comments

@mattleibow
Copy link
Contributor

mattleibow commented May 17, 2018

Description

I have two buttons:

<Button
    Text="Button1"
    Command="{Binding TheCommand}"
    IsEnabled="False" />

<Button
    Text="Button2"
    IsEnabled="False"
    Command="{Binding TheCommand}" />

For some reason, the IsEnabled property is overwritten in Button2 - this enables the button incorrectly.

Button1 is disabled this way, but when I trigger TheCommand.ChangeCanExecute(), the button enables.

Expected Behavior

Both buttons stay disabled.

The matrix of this should be an && operation:

TheCommand.CanExecute() IsEnabled Final State
True True Enabled
True False Disabled
False True Disabled
False False Disabled

Actual Behavior

The button with the command applied later overwrites the enabled state.

Basic Information

  • Version with issue: 2.5.x and 3.0
  • Last known good version: unsure
  • IDE: VS2017
  • Platform Target Frameworks: all
@pauldipietro pauldipietro added this to New in Triage May 17, 2018
@mattleibow
Copy link
Contributor Author

I just checked with native UWP, and the matrix is the platform does - and the order doesn't matter.

@pauldipietro pauldipietro moved this from New to Ready For Work in Triage May 17, 2018
@hartez
Copy link
Contributor

hartez commented May 17, 2018

@pauldipietro I don't think is is a XAML issue. IsEnabledProperty and Command.CanExecute are each changing IsEnabled completely independent of one another. You could fail the matrix above with pure C#.

@mattleibow
Copy link
Contributor Author

I was looking at this and I think the real issue is the fact that the IsEnabled property is being overwritten entirely.
What we can do is make use of the coercing feature and actually use that. When IsEnabled is set, we store the actual value and then run a coerce on it. In the Button, we implement a coerce function to return the Command.CanExecute value. The resulting IsEnabled should be the propertyValue && infusedValue.

class VisualElement {
    BindableProperty IsEnabledProperty = BindableProperty.Create(
        ...,
        coerceValue: OnIsEnabledCoerce);

    static object OnIsEnabledCoerce(BindableObject bindable, object value) {
        var element = (VisualElement)bindable;
        if (element == null)
            return false;
        if ((bool)value) {
            return element.IsEnabledCore;
        } else {
            return false;
        }
    }

    protected virtual bool IsEnabledCore { get { return true; } }
}

class Button {
    protected override bool IsEnabledCore {
        get { return base.IsEnabledCore && CanExecute; }
    }
    bool CanExecute {
        get { return _canExecute; }
        set {
            if (_canExecute != value) {
                _canExecute = value;
                ReCoerceValue(IsEnabledProperty);
            }
        }
    }
    void CommandCanExecuteChanged(object sender, EventArgs eventArgs) {
        CanExecute = Command?.CanExecute(CommandParameter) ?? true;
    }
}

@mattleibow
Copy link
Contributor Author

mattleibow commented May 18, 2018

I tried to do something, but the binding code is just a bit too complex for me at the moment. I could get things to work mostly OK if I stored the RealValue, the CoercedValue and a IsCoerced flag, but things got wonky because I don't quite understand the setting logic too well.

Hopefully this helps someone, somewhere.

@StephaneDelcroix
Copy link
Member

unrelated to xaml, the same can be achieved in code

@TruffelNL
Copy link

It used to matter what order you have. Currently it looks like it's broken all the way. IsEnabled works until I add Command. As soon as I do that, it doesn't look at IsEnabled anymore.

@andreinitescu
Copy link
Contributor

This problem is many years old, I think at least 3.

@StephaneDelcroix Shouldn't this be marked as a bug and not as an enhancement?

@andreinitescu
Copy link
Contributor

From all the XAML frameworks, this problem is only in Xamarin Forms

@samhouts samhouts removed this from Ready For Work in Triage Oct 4, 2018
@samhouts samhouts added this to Under consideration in Enhancements Feb 5, 2019
@samhouts samhouts moved this from Under consideration to Needs Specification in Enhancements Feb 6, 2019
@jonkas2211
Copy link
Contributor

jonkas2211 commented Feb 26, 2019

In Xamarin.Forms v3.4.0.1008975 it completly ignores the order. IsEnabled is always true, when using a Command!

grafik

grafik

grafik

When is this likely to be fixed?
The only workaround is to set the CanExecute of the ICommand right know.

@dansiegel
Copy link
Contributor

perhaps leave it to me to ruin the party here... but this would introduce a MAJOR breaking change in behavior which means you break people without them knowing it... now "Maybe" its a worthwhile break (though I heavily lean towards no)... but can you honestly name a scenario in which you have two buttons attached to the same command where one should be disabled and the other shouldn't... and you're either NOT using a Parameter or using the same Command Parameter?

The entire point of Can Execute is that you're evaluating the parameter null or otherwise to determine if it can/should execute...

@jbachelor
Copy link

This certainly seems like a bug to me... If it cannot be fixed, I would at least propose some sort of warning be added to the documentation of the IsEnabled property and the Button class. I've created a small project to reproduce the issue, as well as provided a few branches to demonstrate ways to avoid the issue by using CanExecute rather than IsEnabled: https://github.com/jbachelor/XamarinFormsIsEnabledIssue

@mattleibow mattleibow added this to New in Triage via automation Mar 10, 2019
@mattleibow mattleibow removed this from Needs Specification in Enhancements Mar 10, 2019
@mattleibow mattleibow removed this from New in Triage Mar 10, 2019
@mattleibow mattleibow added this to New in Triage via automation Mar 10, 2019
@samhouts samhouts added breaking Changes behavior or appearance and removed t/enhancement ➕ labels Mar 11, 2019
@samhouts samhouts moved this from New to Needs Estimate in Triage Mar 14, 2019
@samhouts samhouts added the e/5 🕔 5 label Mar 22, 2019
@samhouts samhouts added inactive Issue is older than 6 months and needs to be retested help wanted We welcome community contributions to any issue, but these might be a good place to start! up-for-grabs We welcome community contributions to any issue, but these might be a good place to start! labels Sep 4, 2019
@adrianknight89
Copy link
Contributor

Issues like this exist in other places too. I remember looking at the min/max values for Slider a long time ago. Validation/coercing happens at the property level so the order of things being set matters. I agree that any fix here will introduce a breaking change. Right now, documentation seems to be the way to go.

@ianbowles
Copy link

It didn't work for me regardless of order. I solved the problem by putting the button in a stacklayout and setting IsEnabled on that, and also setting BackgroundColor on the button from my behaviour so that the disabled state showed properly on the button.

@samhouts samhouts removed the inactive Issue is older than 6 months and needs to be retested label Mar 18, 2020
@pmahend1
Copy link

pmahend1 commented Apr 3, 2020

I confirm its still an issue , its taking order into action

  1. Order 1. Command Order 2. IsEnabled - working as expected
  2. Order1. IsEnabled Order 2. Command not working as expected

I dont know the implications of not setting Command.ChangeCanExecute to Button IsEnabled property internally in Xamarin, as a developer I certainly wont call Command.ChaneCanExecute everytime I want to disable button. If that is what expected then I can understand.

@samhouts samhouts moved this from To do to To do-High impact in Other Ready For Work Jun 1, 2020
@nrooban
Copy link

nrooban commented Jun 14, 2020

Guy, there is an easy fix for this. IsEnable property is only bound to Clicked event. With command it wont work. So the fix is

MyPage.xaml
<Button Text="Next" Clicked="Submit_Clicked" IsEnabled="{Binding hasNotError}}" />

MyPage.xaml.cs
void Submit_Clicked(System.Object sender, System.EventArgs e) { ((MyPageViewModel)BindingContext).SubmitMyFormCommandCommand.Execute(null); }

@mattleibow
Copy link
Contributor Author

mattleibow commented Aug 25, 2020

Although I agree with @dansiegel on the breaking change, I think we also need to consider how styles and state management works. For example, I could have a submit button that sends a message. But, I then have a style that overrides everything and marks the button as disabled.

I do not think that these are mutually exclusive at all. The Command has the ability to disable the button form the VM, and the IsEnabled is controlled from styles in the view.

In defense of this "bug", the IsEnabled is now ignored when there is a command. This was very unexpected.

@William-H-M
Copy link

I just tested this on my own project with the last version, 4.8.0.1451, and seems to be fixed.

The last version I knew it was working was 3.3

@douglassimaodev
Copy link

douglassimaodev commented Feb 20, 2021

Using XF 5.0 error still there, you cant get IsEnable working if you have command

Not Working

<Button Text="Accept offer"  Command="{Binding BindingContext.AcceptCommand, Source={x:Reference MyDescriptionPageName}}" CommandParameter="{Binding Id}" IsEnabled="False"/>

Not Working

<Button Text="Accept offer"  IsEnabled="False" Command="{Binding BindingContext.AcceptCommand, Source={x:Reference MyDescriptionPageName}}" CommandParameter="{Binding Id}" />

obs: MyDescriptionPageName is just my page x:Name to be able to send Parameter

 public ICommand AcceptCommand=> new Command<Guid>(async (Id) => await AcceptJob(Id));

@acaliaro
Copy link

acaliaro commented May 3, 2021

I have the same problem with XF5 today.
On two different buttons, one it works, one does not work

           <StackLayout xct:SideMenuView.Position="MainView" BackgroundColor="White">

                <Button
                    Command="{Binding LoginCommand}"
                    IsEnabled="{Binding IsLoggedIn, Converter={StaticResource InvertedBoolConverter}}"
                    Text="{x:Static resource:AppResource.Login}" />

                <Button
                    Command="{Binding LetturaBarcodeCommand}"
                    IsEnabled="{Binding IsLoggedIn}"
                    Text="{x:Static resource:AppResource.LetturaBarcode}" />

                <Button
                    Command="{Binding LogoutCommand}"
                    IsEnabled="{Binding IsLoggedIn}"
                    Text="{x:Static resource:AppResource.Logout}" />

          
            </StackLayout>

I have this stacklayout with a login and logout button. in login button, IsLoggedIn works fine.
In logout button, IsLoggedIn does not work (button is always enabled).
In logout button, I set IsLoggedIn to false but it disable / enable only login and letturaBarcode button.

To have IsLoggedIn working also with logout button, I have used this workaround in LogoutCommand

                    Task.Run(() =>
                    {
                        IsLoggedIn = false;
                    });

@acaliaro
Copy link

acaliaro commented May 3, 2021

I have undertand the problem

LogoutCommand = new AsyncCommand(LogoutAsync, allowsMultipleExecutions: true);

In LogoutCommand, if I set IsLoggedIn to false and allowsMultipleExecutions is set to false, the button does not disable...

@aschuhardt
Copy link

This still happens in MAUI 6.0.312

@BMarques
Copy link

Like @aschuhardt mentioned, still happens with MAUI indeed, at least on Android. If it wasn't for a comment in Stack Overflow, I'd never have this figured out...

@silmoonsone
Copy link

我尝试过其他方式,在构造函数中创建View Model制定绑定属性的改变操作,会有绑定IsEnabled失败的问题,但是创建Task,在Task中使用UI线程调用绑定操作,会生效,很奇怪。
MoreButton是绑定在Button上的IsEnabled:
QQ截图20220810231015

@samhouts samhouts added the p/0 label Jan 6, 2023
@dantovalphas
Copy link

This still occurs in MAUI, and only when I send the version with Release Mode (for testers) in develop or Debug mode not occurs, I set correctly the command and the property for canExecute command and enable/disabled the button automatically, but... the style no changed, only the button can pressed but style is disabled always... It is desperate and is something that should be corrected, at least using only the command.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
breaking Changes behavior or appearance e/5 🕔 5 help wanted We welcome community contributions to any issue, but these might be a good place to start! m/high impact ⬛ p/0 t/bug 🐛 up-for-grabs We welcome community contributions to any issue, but these might be a good place to start!
Projects
Other Ready For Work
  
To do-High impact
Development

No branches or pull requests