Style.Triggers TargetType="Button" Property="IsEnabled"

NicolasKrierNicolasKrier FRMember ✭✭✭
edited December 2018 in Xamarin.Forms

Hello,

I wanted to give a new try to Triggers to avoid using converters / VisualState / CustomRenderer.

I got a ListView filled with data which contains several buttons. Those buttons have their IsEnabled property binded and they use a style which use a Trigger to change the TextColor / Opacity / BackgroundColor to show to user that a button is either available or not.

It doesn't seems to work as I expect.

Here is the style :
<Style x:Key="ContextualCellButtonStyle" TargetType="Button">
<Style.Triggers>
<Trigger TargetType="Button" Property="IsEnabled" Value="False">
<Setter Property="Text" Value="IsEnabled=False" />
<Setter Property="TextColor" Value="Green" />
<Setter Property="BackgroundColor" Value="Red" />
</Trigger>
<Trigger TargetType="Button" Property="IsEnabled" Value="True">
<Setter Property="Text" Value="IsEnabled=True" />
<Setter Property="TextColor" Value="Red" />
<Setter Property="BackgroundColor" Value="Green" />
</Trigger>
</Style.Triggers>
</Style>

(The values used are not the final one, it's just to debug)
As you can see, when a button is enabled, the text should be IsEnabled=True with a green background and red foreground.
If the button is not enabled, the text should be IsEnabled=False with a red background and green foreground.

In my design time model, I've set the first item to enabled and the second one to disabled :

And in my XAML, I have this :

Here is the result I got :

You can see that when the IsEnabled=False, it uses the trigger IsEnabled=True and when the IsEnabled is set to true, I don't have the style I want too.

I guess the Triggers work when the IsEnabled property changes at runtime but I'm not sure. If it is the case, what do you recommend ? A custom control ? A custom renderer ?
I would like to avoid VisualState because I'd like to use MVVM without code behind for this stuff.

I have already written some custom renderer like this but I thought Trigger would be better in this case so I won't have to create a custom control and a custom renderer :

Any help or suggestion is welcomed.
Thanks for reading so far !

Best Answers

  • NicolasKrierNicolasKrier FR ✭✭✭
    Accepted Answer

    Ok. Thanks. You have a very good alternative, I will mark your response as the answer.

    But I kept another solution because I don't need a specific style right now for the disabled button. I just needed to make it clear to user the user can't be clicked/touched.

    I can set Red/Purple/AnyColor it will not be used but I found after several test on both iOS and Android that setting the TextColor on the Disabled State force the refresh of the button and it get greyed.

    Thanks for your time, I've learned a new thing thanks to you

Answers

  • GaetanFGaetanF USMember ✭✭✭

    Disabling a button freezes its appearance. You have to modify its visual style before disabling it.

  • NicolasKrierNicolasKrier FRMember ✭✭✭

    Thank you @GaetanF

    Ok I see ! It makes sense.
    If I understand, Triggers work for any property except IsEnabled ?

    What would you do ? Create a custom control with another IsEnabledNonFreezing bindable property and use this one in my triggers instead ? Or custom renderer is my only true friend ? :)

  • NicolasKrierNicolasKrier FRMember ✭✭✭
    Accepted Answer

    Ok. Thanks. You have a very good alternative, I will mark your response as the answer.

    But I kept another solution because I don't need a specific style right now for the disabled button. I just needed to make it clear to user the user can't be clicked/touched.

    I can set Red/Purple/AnyColor it will not be used but I found after several test on both iOS and Android that setting the TextColor on the Disabled State force the refresh of the button and it get greyed.

    Thanks for your time, I've learned a new thing thanks to you

  • NicolasKrierNicolasKrier FRMember ✭✭✭
    edited December 2018

    I just came to add that Visual States doesn't work well... Sometimes it works, sometimes it doesn't (just for button control).
    Don't ask me why it works sometimes ^^ (I guess sometimes the states are rendered faster that the button gets disable but most of the times the button is frozen before the visual state are evaluated)
    I don't have any choice that doing a custom control like you did.
    Too bad... :)

  • NicolasKrierNicolasKrier FRMember ✭✭✭
    edited December 2018

    Hey @GaetanF I need your help :)

    I have created this control :

    public class DisableableButton : Button
    {
        private static readonly Color EnabledColor;
        private static readonly Color DisabledColor;
    
        /// <summary>
        /// Static constructor to set the static readonly field only once.
        /// </summary>
        static DisableableButton()
        {
            //App.Current.Resources.TryGetValue("ValidateBackgroundColor", out object color);
            //EnabledColor = (Color)color;
    
            //App.Current.Resources.TryGetValue("AlertBackgroundColor", out color);
            //DisabledColor = (Color)color;
    
            EnabledColor = Color.Green;
            DisabledColor = Color.Red;
        }
    
        public DisableableButton()
        {
            PropertyChanging += DisableableButton_PropertyChanging;
            PropertyChanged += DisableableButton_PropertyChanged;
        }
    
        private void DisableableButton_PropertyChanging(object sender, PropertyChangingEventArgs e)
        {
            // When property is changing, its IsEnabledProperty is still not updated, that's why it checks (IsEnabled) and not (!IsEnabled).
            if (e.PropertyName == nameof(IsEnabled) && IsEnabled)
                TextColor = DisabledColor;
        }
    
        private void DisableableButton_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(IsEnabled) && IsEnabled)
                TextColor = EnabledColor;
        }
    }
    

    This is what I get :

    Notice that the Label are set to grey (I've used a VisualState which is working). That shows the IsEnabled is well set.
    I don't get why the TextColor is set to Green instead of Red.

    Do you use your control with Binding ? If yes, do your binding are called once or are they called after they are rendered ?

  • NicolasKrierNicolasKrier FRMember ✭✭✭

    The workaround I kept is my old one : because I don't need a specific style : the important thing is to see if a button is enable or not. How I display it is just sugar.

    Instead of binding the IsEnabled, I bind Opacity with a Converter that set to 0.3 when it is false. Annnnnd it's done.
    (I check the property in the Execute of the command)

  • HemalathaMarikumarHemalathaMarikumar USMember ✭✭

    What is the use of VisualStateGroupList with Style?. Without that VisualState changes are not reflected only applying the Style. But Without style, we have just added the VisualStateGroup - Worked Fine.

Sign In or Register to comment.