Changing style based on trigger with Xamarin.Forms on WinPHone 8.1

1.3k Views Asked by At

I have the following style

<Style x:Key="GreenButtonStyle" BasedOn="{StaticResource MyButtonBaseStyle}" TargetType="Button">
    <Style.Triggers>
        <Trigger TargetType="Button" Property="IsEnabled" Value="False">
            <Setter Property="BackgroundColor" Value="Silver" />
            <Setter Property="TextColor" Value="Black" />
        </Trigger>
        <Trigger TargetType="Button" Property="IsEnabled" Value="True">
            <Setter Property="BackgroundColor" Value="Green" />
            <Setter Property="TextColor" Value="Orange" />
        </Trigger>
    </Style.Triggers>
</Style>

Which I use in the following view.

<Button x:Name="ConfirmButton" 
    Text="Confirm" 
    Style="{StaticResource GreenButtonStyle}" 
    IsEnabled="{Binding Source={x:Reference Some}, Path=ConfirmEnabled}"
    /> 
<Button x:Name="ToggleButton"
    Text="Toggle" 
    Clicked="Toggle_Clicked"
    />

With the code behind (just snippets)

public partial class SomeView : ContentPage, INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private bool _confirmEnabled = false;
    public bool ConfirmEnabled
    {
        get { return _confirmEnabled; }
    }

    private void Toggle_Clicked(object sender, EventArgs e)
    {
        _confirmEnabled = !_confirmEnabled;
        PropertyChanged(this, new PropertyChangedEventArgs(nameof(ConfirmEnabled)));
    }
}

When the View comes into play, everything is ok with the button inactive. When I click the toggle button, the font changes color to Orange as intended, but the background doesn't change color to green.

If I click the toggle twice (disable then enable) the font is Orange and background is finally green.

Anybody have any idea why the background color isn't changed on the first change of ConfirmEnabled to true?

2

There are 2 best solutions below

0
GroThar On BEST ANSWER

Ended with implementing my own button control which is basically two buttons on top of each other and then depending on if it's enabled or disabled I hide or show the other one.

<ContentView xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MyProject.Views._Controls.MyButton">

    <Grid>
        <Button x:Name="WhenEnabledButton"
                Clicked="WhenEnabledButton_Clicked"
                Text="WhenEnabled"></Button>
        <Button x:Name="WhenDisabledButton"
                Text="WhenDisabled"></Button>
        <Label x:Name="DisabledButtonCoveringLabel" BackgroundColor="Transparent"></Label>
    </Grid>
</ContentView>

And the code behind

namespace MyProject.Views._Controls
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class MyButton : ContentView
    {
        #region Property Bindings
        public static readonly BindableProperty CommandProperty = BindableProperty.Create("Command", typeof(ICommand), typeof(MyButton),
            null, propertyChanged: (bo, o, n) => ((MyButton)bo).OnCommandChanged());

        public static readonly BindableProperty CommandParameterProperty = BindableProperty.Create("CommandParameter", typeof(object),
            typeof(MyButton), null,
        propertyChanged: (bindable, oldvalue, newvalue) => ((MyButton)bindable).CommandCanExecuteChanged(bindable, EventArgs.Empty));

        public static readonly new BindableProperty IsEnabledProperty =
            BindableProperty.Create(
                propertyName: "IsEnabled",
                returnType: typeof(Boolean),
                declaringType: typeof(MyButton),
                defaultValue: false,
                defaultBindingMode: BindingMode.OneWay,
                propertyChanged: IsEnabledChanged);

        private static void IsEnabledChanged(BindableObject bindable, object oldValue, object newValue)
        {
            var control = (MyButton)bindable;
            bool enabled = (bool)newValue;
            control.WhenEnabledButton.IsVisible = enabled;
            control.WhenDisabledButton.IsVisible = !enabled;

            if (Device.RuntimePlatform == Device.WinPhone || Device.RuntimePlatform == Device.WinRT)
            {
                control.DisabledButtonCoveringLabel.IsVisible = !enabled;
            }
        }

        public static readonly BindableProperty TextProperty =
            BindableProperty.Create(
                propertyName: "Text",
                returnType: typeof(string),
                declaringType: typeof(MyButton),
                defaultValue: string.Empty,
                defaultBindingMode: BindingMode.OneWay,
                propertyChanged: TextChanged);

        private static void TextChanged(BindableObject bindable, object oldValue, object newValue)
        {
            var control = (MyButton)bindable;
            control.WhenEnabledButton.Text = (string)newValue;
            control.WhenDisabledButton.Text = (string)newValue;
        }

        public static readonly BindableProperty EnabledStyleProperty =
            BindableProperty.Create(
                propertyName: "EnabledStyle",
                returnType: typeof(Style),
                declaringType: typeof(MyButton),
                defaultBindingMode: BindingMode.OneWay,
                propertyChanged: EnabledStyleChanged);

        private static void EnabledStyleChanged(BindableObject bindable, object oldValue, object newValue)
        {
            var control = (MyButton)bindable;
            control.WhenEnabledButton.Style = (Style)newValue;
        }

        public static readonly BindableProperty DisabledStyleProperty =
            BindableProperty.Create(
                propertyName: "DisabledStyle",
                returnType: typeof(Style),
                declaringType: typeof(MyButton),
                defaultBindingMode: BindingMode.OneWay,
                propertyChanged: DisabledStyleChanged);

        private static void DisabledStyleChanged(BindableObject bindable, object oldValue, object newValue)
        {
            var control = (MyButton)bindable;
            control.WhenDisabledButton.Style = (Style)newValue;
        }
        #endregion


        public ICommand Command
        {
            get { return (ICommand)GetValue(CommandProperty); }
            set { SetValue(CommandProperty, value); }
        }

        public object CommandParameter
        {
            get { return GetValue(CommandParameterProperty); }
            set { SetValue(CommandParameterProperty, value); }
        }

        public string Text
        {
            get
            {
                return this.GetValue<string>(TextProperty);
            }

            set
            {
                this.SetValue(TextProperty, value);
            }
        }

        public new bool IsEnabled
        {
            get
            {
                return this.GetValue<bool>(IsEnabledProperty);
            }

            set
            {
                this.SetValue(IsEnabledProperty, value);
            }
        }

        #region Click event

        public delegate void ClickedHandler(object sender, EventArgs e);
        public event ClickedHandler Clicked;

        private void WhenEnabledButton_Clicked(object sender, EventArgs e)
        {
            if (Clicked != null)
                this.Clicked.Invoke(sender, e);
            else
                this.Command.Execute(this.CommandParameter);
        }

        void CommandCanExecuteChanged(object sender, EventArgs eventArgs)
        {
            ICommand cmd = Command;
            if (cmd != null)
                IsEnabled = cmd.CanExecute(CommandParameter);
        }

        void OnCommandChanged()
        {
            if (Command != null)
            {
                Command.CanExecuteChanged += CommandCanExecuteChanged;
                CommandCanExecuteChanged(this, EventArgs.Empty);
            }
            else
                IsEnabled = true;
        }

        #endregion

        public MyButton()
        {
            InitializeComponent();
            if (Device.RuntimePlatform == Device.Android)
            {
                DisabledButtonCoveringLabel.IsVisible = false;
                WhenDisabledButton.IsEnabled = false;
            }
            else if (Device.RuntimePlatform == Device.WinPhone || Device.RuntimePlatform == Device.WinRT)
            {
                DisabledButtonCoveringLabel.IsVisible = true;
                WhenDisabledButton.IsEnabled = true;
            }

            this.SetValue(EnabledStyleProperty, Application.Current.Resources["GreenEnabledButtonStyle"]);
            this.SetValue(DisabledStyleProperty, Application.Current.Resources["GreenDisabledButtonStyle"]);
        }
    }
}
0
sme On

I would recommend using a MVVM approach to your app design. Create a class called BaseViewModel.cs and another called SomeViewModel.cs, and write them like so:

BaseViewModel.cs, which every view model should inherit from. It allows your to use PropertyChanged events:

public class BaseViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

SomeViewModel.cs. Now every time you change the value of ConfirmEnabled, the view can automatically detect the change once you have set up binding:

public class SomeViewModel : BaseViewModel
{
    private bool _confirmEnabled = false;

    public bool ConfirmEnabled
    {
        get { return _confirmEnabled; }
        set 
        { 
            _confirmEnabled = value;
            OnPropertyChanged(nameof(ConfirmEnabled));
        }
    }
}

Now, for your page SomeView.cs:

public partial class SomeView : ContentPage, INotifyPropertyChanged
{
    SomeViewModel _viewModel;

    public SomeView()
    {
        InitializeComponent();
        _viewModel = new SomeViewModel();
        BindingContext = _viewModel;
    }

    private void Toggle_Clicked(object sender, EventArgs e)
    {
        _viewModel.ConfirmEnabled = !_viewModel.ConfirmEnabled;
    }
}

And your button code:

    <Button x:Name="ConfirmButton" 
        Text="Confirm" 
        Style="{StaticResource GreenButtonStyle}" 
        IsEnabled="{Binding ConfirmEnabled}"
        /> 
    <Button x:Name="ToggleButton"
        Text="Toggle" 
        Clicked="Toggle_Clicked"
        />