flexbutton icon indicating copy to clipboard operation
flexbutton copied to clipboard

NullReferenceException when using global Style

Open NickeManarin opened this issue 6 years ago • 3 comments

I'm getting a NullReferenceException when using a global Style. The same does not happen when the Style has a key and it's applied to the Button itself.

This is the Style:

<Style TargetType="{x:Type l:FlexButton}">
    <Setter Property="FontSize" Value="16"/>
    <Setter Property="BackgroundColor" Value="{DynamicResource Color.Foreground}"/>
    <Setter Property="ForegroundColor" Value="{DynamicResource Color.Background.Odd}"/>
    <Setter Property="FontFamily" Value="{StaticResource Font.Heavy}"/>
    <Setter Property="CornerRadius" Value="10"/>
</Style>

This is the Button:

<l:FlexButton WidthRequest="25" HeightRequest="25" CornerRadius="38" Text="X" FontSize="10" Padding="0" VerticalOptions="Start" HorizontalOptions="End"/>

The Exception happens when executing this code:

[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Forms.Build.Tasks.XamlG", "0.0.0.0")]
private void InitializeComponent() 
{
    global::Xamarin.Forms.Xaml.Extensions.LoadFromXaml(this, typeof(PhotosView));
}

StackTrace:

   at Flex.Controls.FlexButton.SetButtonMode()
   at Flex.Controls.FlexButton.OnPropertyChanged(String propertyName)
   at Xamarin.Forms.BindableObject.SetValueActual(BindableProperty property, BindablePropertyContext context, Object value, Boolean currentlyApplying, SetValueFlags attributes, Boolean silent)
   at Xamarin.Forms.BindableObject.SetValueCore(BindableProperty property, Object value, SetValueFlags attributes, SetValuePrivateFlags privateAttributes)
   at Xamarin.Forms.BindableObject.SetValue(BindableProperty property, Object value, Boolean fromStyle, Boolean checkAccess)
   at Xamarin.Forms.Setter.Apply(BindableObject target, Boolean fromStyle)
   at Xamarin.Forms.Style.ApplyCore(BindableObject bindable, Style basedOn)
   at Xamarin.Forms.Style.Xamarin.Forms.IStyle.Apply(BindableObject bindable)
   at Xamarin.Forms.MergedStyle.SetStyle(IStyle implicitStyle, IList`1 classStyles, IStyle style)
   at Xamarin.Forms.MergedStyle.OnImplicitStyleChanged()
   at Xamarin.Forms.MergedStyle.<RegisterImplicitStyles>b__30_0(BindableObject bindable, Object oldvalue, Object newvalue)
   at Xamarin.Forms.BindableObject.SetValueActual(BindableProperty property, BindablePropertyContext context, Object value, Boolean currentlyApplying, SetValueFlags attributes, Boolean silent)
   at Xamarin.Forms.BindableObject.SetValueCore(BindableProperty property, Object value, SetValueFlags attributes, SetValuePrivateFlags privateAttributes)
   at Xamarin.Forms.Element.OnSetDynamicResource(BindableProperty property, String key)
   at Xamarin.Forms.BindableObject.SetDynamicResource(BindableProperty property, String key, Boolean fromStyle)
   at Xamarin.Forms.MergedStyle.RegisterImplicitStyles()
   at Xamarin.Forms.MergedStyle..ctor(Type targetType, BindableObject target)
   at Xamarin.Forms.NavigableElement..ctor()
   at Xamarin.Forms.VisualElement..ctor()
   at Xamarin.Forms.View..ctor()
   at Xamarin.Forms.Layout..ctor()
   at Flex.Controls.FlexButton..ctor()
   at MyApp.View.RegistrationSteps.PhotosView.InitializeComponent()
   at MyApp.View.RegistrationSteps.PhotosView..ctor()
   at MyApp.ViewModel.SignUpViewModel.Load_Executed()
   at Xamarin.Forms.Command.<>c__DisplayClass4_0.<.ctor>b__0(Object o)
   at Xamarin.Forms.Command.Execute(Object parameter)
   at MyApp.View.SignUp.OnAppearing()
   at Xamarin.Forms.Page.SendAppearing()
   at Xamarin.Forms.Page.SendAppearing()
   at Xamarin.Forms.Platform.UWP.NavigationPageRenderer.OnLoaded(Object sender, RoutedEventArgs args)

Please complete the following information:

  • Which version of the FlexButton do you use? 0.9.1
  • Which version of Xamarin.Forms do you use? 3.6.0.293080
  • Which OS are you talking about? Android and UWP

NickeManarin avatar Apr 26 '19 12:04 NickeManarin

@NickeManarin Sometime code behind crash beetwen initlized and not yet.

I fixed code below xaml and cs file. You can try this to use global style

XAML ` <ContentView x:Name="this" x:Class="Flex.Controls.FlexButton" xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:extensions="clr-namespace:Flex.Extensions" xmlns:controls="clr-namespace:Flex.Controls">

<controls:GestureFrame
    x:Name="Border"
    BackgroundColor="{Binding Source={x:Reference this}, Path=BorderColor}"
    CornerRadius="{Binding Source={x:Reference this}, Path=CornerRadius}"
    Padding="{Binding Source={x:Reference this}, Path=BorderThickness}"
    HasShadow="{Binding Source={x:Reference this}, Path=HasShadow}">
    
    <controls:GestureFrame.BorderColor>
        <!--- HACK: Temporary Bugfix for Xamarin.Forms 3.0 bug https://github.com/xamarin/Xamarin.Forms/issues/2634 -->
        <OnPlatform x:TypeArguments="Color" Default="Transparent">
            <!--<On Platform="Android" Value="{Binding Source={x:Reference this}, Path=BorderColor}" />-->
        </OnPlatform>
    </controls:GestureFrame.BorderColor>

    <controls:GestureFrame.GestureRecognizers>
        <extensions:TouchGestureRecognizer x:Name="TouchRecognizer" />
    </controls:GestureFrame.GestureRecognizers>
    
    <controls:GestureFrame
        x:Name="Container"
        BackgroundColor="{Binding Source={x:Reference this}, Path=BackgroundColor}"
        CornerRadius="{Binding Source={x:Reference this}, Path=InnerCornerRadius}"
        Padding="0"
        Margin="0"
        HasShadow="false">
        <controls:GestureFrame.BorderColor>
            <!--- HACK: Temporary Bugfix for Xamarin.Forms 3.0 bug https://github.com/xamarin/Xamarin.Forms/issues/2634 -->
            <OnPlatform x:TypeArguments="Color" Default="Transparent">
                <!--<On Platform="Android" Value="{Binding Source={x:Reference this}, Path=BackgroundColor}" />-->
            </OnPlatform>
        </controls:GestureFrame.BorderColor>
        
        <AbsoluteLayout> 
            <FlexLayout 
                x:Name="ContainerContent"                    
                Margin="{Binding Source={x:Reference this}, Path=Padding}"
                Direction="Row" 
                AlignItems="Center" 
                JustifyContent="Center">
                <FlexLayout.Triggers>
                    <MultiTrigger TargetType="FlexLayout" >
                        <MultiTrigger.Conditions>
                            <BindingCondition Binding="{Binding Source={x:Reference this}, Path=mode}" Value="IconWithText" />
                            <BindingCondition Binding="{Binding Source={x:Reference this}, Path=IconOrientation}" Value="Top" />
                        </MultiTrigger.Conditions>
                        <Setter Property="Direction" Value="Column" />
                    </MultiTrigger>
                    <MultiTrigger TargetType="FlexLayout" >
                        <MultiTrigger.Conditions>
                            <BindingCondition Binding="{Binding Source={x:Reference this}, Path=mode}" Value="IconWithText" />
                            <BindingCondition Binding="{Binding Source={x:Reference this}, Path=IconOrientation}" Value="Left" />
                        </MultiTrigger.Conditions>
                        <Setter Property="Direction" Value="Row" />
                    </MultiTrigger>
                    <MultiTrigger TargetType="FlexLayout" >
                        <MultiTrigger.Conditions>
                            <BindingCondition Binding="{Binding Source={x:Reference this}, Path=mode}" Value="IconWithText" />
                            <BindingCondition Binding="{Binding Source={x:Reference this}, Path=IconOrientation}" Value="Right" />
                        </MultiTrigger.Conditions>
                        <Setter Property="Direction" Value="RowReverse" />
                    </MultiTrigger>
                </FlexLayout.Triggers>

                <Image 
                    x:Name="ButtonIcon" 
                    Margin="{Binding Source={x:Reference this}, Path=IconPadding}"
                    Source="{Binding Source={x:Reference this}, Path=Icon}" >
                    <Image.Triggers>
                        <DataTrigger TargetType="Image" Binding="{Binding Source={x:Reference this}, Path=mode}" Value="IconOnly">
                            <Setter Property="Grid.ColumnSpan" Value="2" />
                        </DataTrigger>
                        <DataTrigger TargetType="Image" Binding="{Binding Source={x:Reference this}, Path=mode}" Value="IconWithText">
                            <Setter Property="IsVisible" Value="True" />
                        </DataTrigger>
                        <DataTrigger TargetType="Image" Binding="{Binding Source={x:Reference this}, Path=mode}" Value="TextOnly">
                            <Setter Property="Grid.ColumnSpan" Value="1" />
                            <Setter Property="IsVisible" Value="False" />
                        </DataTrigger>
                    </Image.Triggers>
                </Image>

                <Label
                    x:Name="ButtonText"
                    HorizontalTextAlignment="Center"
                    MaxLines="{Binding Source={x:Reference this}, Path=MaxLines}"
                    Text="{Binding Source={x:Reference this}, Path=Text}"
                    FontSize="{Binding Source={x:Reference this}, Path=FontSize}"
                    FontAttributes="{Binding Source={x:Reference this}, Path=FontAttributes}"
                    FontFamily="{Binding Source={x:Reference this}, Path=FontFamily}"
                    TextColor="{Binding Source={x:Reference this}, Path=ForegroundColor}" >
                    <Label.Triggers>
                        <DataTrigger TargetType="Label" Binding="{Binding Source={x:Reference this}, Path=mode}" Value="IconOnly">
                            <Setter Property="Grid.Column" Value="1" />
                            <Setter Property="IsVisible" Value="False" />
                        </DataTrigger>
                        <DataTrigger TargetType="Label" Binding="{Binding Source={x:Reference this}, Path=mode}" Value="IconWithText">
                            <Setter Property="Grid.Column" Value="0" />
                            <Setter Property="Grid.ColumnSpan" Value="1" />
                            <Setter Property="IsVisible" Value="True" />
                        </DataTrigger>
                        <DataTrigger TargetType="Label" Binding="{Binding Source={x:Reference this}, Path=mode}" Value="TextOnly">
                            <Setter Property="Grid.ColumnSpan" Value="2" />
                            <Setter Property="Grid.Column" Value="0" />
                            <Setter Property="IsVisible" Value="True" />
                        </DataTrigger>
                    </Label.Triggers>
                </Label>
            </FlexLayout>                
        </AbsoluteLayout>
    </controls:GestureFrame>
</controls:GestureFrame>
`

Code behind: ` [assembly: XamlCompilation(XamlCompilationOptions.Compile)] namespace Flex.Controls { public partial class FlexButton : ContentView { //ButtonMode mode; bool userChangedPadding; bool userChangedIconPadding;

    public void SetChangedPadding(bool val)
    {
        userChangedPadding = val;
    }

    public void SetChangedIconPadding(bool val)
    {
        userChangedIconPadding = val;
    }

    #region Bindable Properties

    public static readonly BindableProperty modeProperty = BindableProperty.Create(nameof(mode), typeof(ButtonMode), typeof(FlexButton), ButtonMode.TextOnly);
    public ButtonMode mode
    {
        get => (ButtonMode)GetValue(modeProperty);
        set => SetValue(modeProperty, value);
    }

    public static readonly new BindableProperty BackgroundColorProperty = BindableProperty.Create(nameof(BackgroundColor), typeof(Color), typeof(FlexButton), Color.Transparent);
    public new Color BackgroundColor
    {
        get => (Color)GetValue(BackgroundColorProperty);
        set => SetValue(BackgroundColorProperty, value);
    }

    public static readonly BindableProperty HighlightBackgroundColorProperty = BindableProperty.Create(nameof(HighlightBackgroundColor), typeof(Color), typeof(FlexButton), Color.Transparent);
    public Color HighlightBackgroundColor
    {
        get => (Color)GetValue(HighlightBackgroundColorProperty);
        set => SetValue(HighlightBackgroundColorProperty, value);
    }

    public static readonly BindableProperty ForegroundColorProperty = BindableProperty.Create(nameof(ForegroundColor), typeof(Color), typeof(FlexButton), Color.Accent);
    public Color ForegroundColor
    {
        get => (Color)GetValue(ForegroundColorProperty);
        set => SetValue(ForegroundColorProperty, value);
    }

    public static readonly BindableProperty HighlightForegroundColorProperty = BindableProperty.Create(nameof(HighlightForegroundColor), typeof(Color), typeof(FlexButton), Color.White);
    public Color HighlightForegroundColor
    {
        get => (Color)GetValue(HighlightForegroundColorProperty);
        set => SetValue(HighlightForegroundColorProperty, value);
    }

    // Border Properties

    public static readonly BindableProperty BorderColorProperty = BindableProperty.Create(nameof(BorderColor), typeof(Color), typeof(FlexButton), Color.Transparent);
    public Color BorderColor
    {
        get => (Color)GetValue(BorderColorProperty);
        set => SetValue(BorderColorProperty, value);
    }

    public static readonly BindableProperty HighlightBorderColorProperty = BindableProperty.Create(nameof(HighlightBorderColor), typeof(Color), typeof(FlexButton), Color.Transparent);
    public Color HighlightBorderColor
    {
        get => (Color)GetValue(HighlightBorderColorProperty);
        set => SetValue(HighlightBorderColorProperty, value);
    }

    public static readonly BindableProperty BorderThicknessProperty = BindableProperty.Create(nameof(BorderThickness), typeof(Thickness), typeof(FlexButton), new Thickness(0));
    public Thickness BorderThickness
    {
        get => (Thickness)GetValue(BorderThicknessProperty);
        set => SetValue(BorderThicknessProperty, value);
    }

    public static readonly BindableProperty CornerRadiusProperty = BindableProperty.Create(nameof(CornerRadius), typeof(int), typeof(FlexButton), 0);
    public int CornerRadius
    {
        get => (int)GetValue(CornerRadiusProperty);
        set => SetValue(CornerRadiusProperty, value);
    }
    public int InnerCornerRadius { get; private set; }

    // Icon Properties

    public static readonly BindableProperty IconProperty = BindableProperty.Create(nameof(Icon), typeof(ImageSource), typeof(FlexButton), null);
    public ImageSource Icon
    {
        get => (ImageSource)GetValue(IconProperty);
        set => SetValue(IconProperty, value);
    }

    public static readonly BindableProperty IconOrientationProperty = BindableProperty.Create(nameof(IconOrientation), typeof(IconOrientation), typeof(FlexButton), IconOrientation.Left);
    public IconOrientation IconOrientation
    {
        get => (IconOrientation)GetValue(IconOrientationProperty);
        set => SetValue(IconOrientationProperty, value);
    }

    public static readonly BindableProperty IconTintEnabledProperty = BindableProperty.Create(nameof(IconTintEnabled), typeof(bool), typeof(FlexButton), true);
    public bool IconTintEnabled
    {
        get => (bool)GetValue(IconTintEnabledProperty);
        set => SetValue(IconTintEnabledProperty, value);
    }

    // Text Properties

    public static readonly BindableProperty TextProperty = BindableProperty.Create(nameof(Text), typeof(string), typeof(FlexButton), string.Empty);
    public string Text
    {
        get => (string)GetValue(TextProperty);
        set => SetValue(TextProperty, value);
    }

    public static readonly BindableProperty FontSizeProperty = BindableProperty.Create(nameof(FontSize), typeof(double), typeof(FlexButton), Device.GetNamedSize(NamedSize.Default, typeof(Label)));
    [TypeConverter(typeof(FontSizeConverter))]
    public double FontSize
    {
        get => (double)GetValue(FontSizeProperty);
        set => SetValue(FontSizeProperty, value);
    }

    public static readonly BindableProperty FontAttributesProperty = BindableProperty.Create(nameof(FontAttributes), typeof(FontAttributes), typeof(FlexButton), (FontAttributes)Label.FontAttributesProperty.DefaultValue);
    public FontAttributes FontAttributes
    {
        get => (FontAttributes)GetValue(FontAttributesProperty);
        set => SetValue(FontAttributesProperty, value);
    }

    public static readonly BindableProperty FontFamilyProperty = BindableProperty.Create(nameof(FontFamily), typeof(string), typeof(FlexButton), (string)Label.FontFamilyProperty.DefaultValue);
    public string FontFamily
    {
        get => (string)GetValue(FontFamilyProperty);
        set => SetValue(FontFamilyProperty, value);
    }

    public static readonly BindableProperty MaxLinesProperty = BindableProperty.Create(nameof(MaxLines), typeof(int), typeof(FlexButton), 1);
    public int MaxLines
    {
        get => (int)GetValue(MaxLinesProperty);
        set => SetValue(MaxLinesProperty, value);
    }

    // Toggle Properties

    public static readonly BindableProperty ToggleModeProperty = BindableProperty.Create(nameof(ToggleMode), typeof(bool), typeof(FlexButton), false);
    public bool ToggleMode
    {
        get => (bool)GetValue(ToggleModeProperty);
        set => SetValue(ToggleModeProperty, value);
    }

    public static readonly BindableProperty IsToggledProperty = BindableProperty.Create(nameof(IsToggled), typeof(bool), typeof(FlexButton), false, BindingMode.TwoWay);
    public bool IsToggled
    {
        get => (bool)GetValue(IsToggledProperty);
        set => SetValue(IsToggledProperty, value);
    }

    // Other Properties

    public static readonly new BindableProperty PaddingProperty = BindableProperty.Create(nameof(Padding), typeof(Thickness), typeof(FlexButton), new Thickness(5,3), propertyChanged: (sender, obj, newVal) =>
    {
        //(sender as FlexButton).userChangedPadding = true;
        //(sender as FlexButton).SetButtonMode();
    });
    public new Thickness Padding
    {
        get => (Thickness)GetValue(PaddingProperty);
        set => SetValue(PaddingProperty, value);
    }

    public static readonly BindableProperty IconPaddingProperty = BindableProperty.Create(nameof(IconPadding), typeof(Thickness), typeof(FlexButton), new Thickness(2));
    public Thickness IconPadding
    {
        get => (Thickness)GetValue(IconPaddingProperty);
        set => SetValue(IconPaddingProperty, value);
    }

    public static readonly BindableProperty HasShadowProperty = BindableProperty.Create(nameof(HasShadow), typeof(bool), typeof(FlexButton), false);
    public bool HasShadow
    {
        get => (bool)GetValue(HasShadowProperty);
        set => SetValue(HasShadowProperty, value);
    }

    #endregion

    #region Commands

    public static readonly BindableProperty ClickedCommandProperty = BindableProperty.Create(nameof(ClickedCommand), typeof(ICommand), typeof(FlexButton), null, propertyChanged: (bindable, oldValue, newValue) => ((FlexButton)bindable).OnClickOrTouchedDownCommandPropertyChanged());
    public ICommand ClickedCommand
    {
        get => (ICommand)GetValue(ClickedCommandProperty);
        set => SetValue(ClickedCommandProperty, value);
    }

    public static readonly BindableProperty ClickedCommandParameterProperty = BindableProperty.Create(nameof(ClickedCommandParameter), typeof(object), typeof(FlexButton), null, propertyChanged: (bindable, oldValue, newValie) => ((FlexButton)bindable).CommandCanExecuteChanged(bindable, EventArgs.Empty));
    public object ClickedCommandParameter
    {
        get => GetValue(ClickedCommandParameterProperty);
        set => SetValue(ClickedCommandParameterProperty, value);
    }

    public static BindableProperty TouchedDownCommandProperty = BindableProperty.Create(nameof(TouchedDownCommand), typeof(ICommand), typeof(FlexButton), null);
    public ICommand TouchedDownCommand
    {
        get { return (ICommand)GetValue(TouchedDownCommandProperty); }
        set { SetValue(TouchedDownCommandProperty, value); }
    }

    public static readonly BindableProperty TouchedDownCommandParameterProperty = BindableProperty.Create(nameof(TouchedDownCommandParameter), typeof(object), typeof(FlexButton), null, propertyChanged: (bindable, oldValue, newValie) => ((FlexButton)bindable).CommandCanExecuteChanged(bindable, EventArgs.Empty));
    public object TouchedDownCommandParameter
    {
        get => GetValue(TouchedDownCommandParameterProperty);
        set => SetValue(TouchedDownCommandParameterProperty, value);
    }

    public static BindableProperty TouchedUpCommandProperty = BindableProperty.Create(nameof(TouchedUpCommand), typeof(ICommand), typeof(FlexButton), null);
    public ICommand TouchedUpCommand
    {
        get => (ICommand)GetValue(TouchedUpCommandProperty);
        set => SetValue(TouchedUpCommandProperty, value);
    }

    public static readonly BindableProperty TouchedUpCommandParameterProperty = BindableProperty.Create(nameof(TouchedUpCommandParameter), typeof(object), typeof(FlexButton), null, propertyChanged: (bindable, oldValue, newValie) => ((FlexButton)bindable).CommandCanExecuteChanged(bindable, EventArgs.Empty));
    public object TouchedUpCommandParameter
    {
        get => GetValue(TouchedUpCommandParameterProperty);
        set => SetValue(TouchedUpCommandParameterProperty, value);
    }

    #endregion

    #region Events

    protected override void OnBindingContextChanged()
    {
        base.OnBindingContextChanged();
    }

    protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        base.OnPropertyChanged(propertyName);
        CalMode();
    }

    void OnClickOrTouchedDownCommandPropertyChanged()
    {
        if (ClickedCommand != null)
            ClickedCommand.CanExecuteChanged += CommandCanExecuteChanged;

        if (TouchedDownCommand != null)
            TouchedDownCommand.CanExecuteChanged += CommandCanExecuteChanged;

        CommandCanExecuteChanged(this, EventArgs.Empty);
    }

    void CommandCanExecuteChanged(object sender, EventArgs e)
    {
        // Define IsEnabled state
        var canExecuteClick = ClickedCommand?.CanExecute(ClickedCommandParameter);
        var canExecuteTouchedDown = TouchedDownCommand?.CanExecute(TouchedDownCommandParameter);

        if (canExecuteClick != null && canExecuteTouchedDown != null)
            IsEnabled = canExecuteClick == true && canExecuteTouchedDown == true;
        else
            IsEnabled = canExecuteClick == true || canExecuteTouchedDown == true;
    }

    protected override void OnPropertyChanging([CallerMemberName] string propertyName = null)
    {
        // Unsubscribe from command events when Command changes
        if (propertyName == ClickedCommandProperty.PropertyName && ClickedCommand != null)
            ClickedCommand.CanExecuteChanged -= CommandCanExecuteChanged;
        if (propertyName == TouchedDownCommandProperty.PropertyName && TouchedDownCommandProperty != null)
            TouchedDownCommand.CanExecuteChanged -= CommandCanExecuteChanged;

        base.OnPropertyChanging(propertyName);
    }

    #endregion

    void CalMode()
    {
        switch (IconOrientation)
        {
            case IconOrientation.Top: IconPadding = new Thickness(0, 0, 0, 6); break;
            case IconOrientation.Left: IconPadding = new Thickness(0, 0, 6, 0); break;
            case IconOrientation.Right: IconPadding = new Thickness(6, 0, 0, 0); break;
        }
        InnerCornerRadius = Math.Max(0, CornerRadius - (int)Math.Max(Math.Max(BorderThickness.Left, BorderThickness.Top), Math.Max(BorderThickness.Right, BorderThickness.Bottom)));
        if (Icon != null && Text.Length > 0)
        {
            mode = ButtonMode.IconWithText;
        }
        else if (Icon != null && Text.Length == 0)
        {
            mode = ButtonMode.IconOnly;
        }
        else if (Icon == null && Text.Length > 0)
        {
            mode = ButtonMode.TextOnly;
        }
        SetButtonMode();
    }

    void SetButtonMode()
    {
        if (Icon != null && Text.Length > 0)
        {
            mode = ButtonMode.IconWithText;
        }
        else if (Icon != null && Text.Length == 0)
        {
            mode = ButtonMode.IconOnly;
        }
        else if (Icon == null && Text.Length > 0)
        {
            mode = ButtonMode.TextOnly;
        }

        switch (mode)
        {
            case ButtonMode.IconOnly:
                if (ButtonIcon != null && ButtonText != null)
                {
                    // Configure Container
                    //ContainerContent.HorizontalOptions = LayoutOptions.Fill;
                    Grid.SetColumnSpan(ButtonIcon, 2);
                    Grid.SetColumn(ButtonText, 1);

                    // Set Visibilities
                    ButtonText.IsVisible = false;
                    ButtonIcon.IsVisible = true;
                }

                // Adjust Default Padding
                if (!userChangedPadding)
                {
                    Padding = new Thickness(2);
                    userChangedPadding = false; // Set this back to false, as the above line triggers OnPropertyChanged 
                }
                

                break;

            case ButtonMode.IconWithText:

                // Configure Container
                switch (IconOrientation)
                {
                    case IconOrientation.Top:
                        if(ContainerContent != null)
                            ContainerContent.Direction = FlexDirection.Column;
                        break;

                    case IconOrientation.Left:
                        if (ContainerContent != null)
                            ContainerContent.Direction = FlexDirection.Row;
                        break;

                    case IconOrientation.Right:
                        if (ContainerContent != null)
                            ContainerContent.Direction = FlexDirection.RowReverse;
                        break;
                }

                if (ButtonIcon != null && ButtonText != null)
                {
                    // Set Visibilities
                    ButtonText.IsVisible = true;
                    ButtonIcon.IsVisible = true;
                }

                // Adjust Default Padding
                /*
                if (!userChangedPadding)
                {
                    Padding = new Thickness(HeightRequest * .1, HeightRequest * .3);
                    userChangedPadding = false; // Set this back to false, as the above line triggers OnPropertyChanged 
                }
                */
                if (!userChangedIconPadding)
                {
                    switch (IconOrientation)
                    {
                        case IconOrientation.Top: IconPadding = new Thickness(0, 0, 0, 6); break;
                        case IconOrientation.Left: IconPadding = new Thickness(0, 0, 6, 0); break;
                        case IconOrientation.Right: IconPadding = new Thickness(6, 0, 0, 0); break;
                    }
                }

                break;

            case ButtonMode.TextOnly:

                // Configure Container
                if (ButtonIcon != null && ButtonText != null)
                {
                    //ContainerContent.HorizontalOptions = LayoutOptions.FillAndExpand;
                    Grid.SetColumnSpan(ButtonIcon, 1);
                    Grid.SetColumnSpan(ButtonText, 2);
                    Grid.SetColumn(ButtonText, 0);

                    // Set Visibilities
                    ButtonText.IsVisible = true;
                    ButtonIcon.IsVisible = false;
                }
                    

                // Adjust Default Padding
                /*
                if (!userChangedPadding)
                {
                    Padding = new Thickness(20, 0);
                    userChangedPadding = false; // Set this back to false, as the above line triggers OnPropertyChanged 
                }
                */
                break;
        }

        // HACK: Horrible Hack, that makes the Xamarin.Forms Previewer work, who seems to give up some Binding support
        // since Xamarin.Forms 2.5.1
        try
        {
            if (Border != null)
            {
                Border.BackgroundColor = BorderColor;
                Border.CornerRadius = CornerRadius;
                Border.Padding = BorderThickness;
                Border.HasShadow = HasShadow;
            }

            if (Container != null)
            {
                Container.BackgroundColor = BackgroundColor;
                Container.CornerRadius = InnerCornerRadius;
                ContainerContent.Margin = Padding;
            }

            if (ButtonText != null)
            {
                ButtonText.Text = Text;
                ButtonText.FontSize = FontSize;
                ButtonText.FontAttributes = FontAttributes;
                ButtonText.FontFamily = FontFamily;
                ButtonText.TextColor = ForegroundColor;
                ButtonText.MaxLines = MaxLines;
            }
            
            if(ButtonIcon != null)
                ButtonIcon.Margin = IconPadding;
        }
        catch (Exception ex)
        {

        }

        if (ToggleMode)
        {
            Highlight(IsToggled);
        }

        // Calculate inner corner radius
        // Use the outer radius minus the max thickness of a single direction
        InnerCornerRadius = Math.Max(0, CornerRadius - (int)Math.Max(Math.Max(BorderThickness.Left, BorderThickness.Top), Math.Max(BorderThickness.Right, BorderThickness.Bottom)));
        if(Container != null)
            Container.CornerRadius = InnerCornerRadius;

        //ColorIcon(ForegroundColor);
    }

    public event EventHandler<EventArgs> TouchedDown;
    public event EventHandler<EventArgs> TouchedUp;
    public event EventHandler<EventArgs> Clicked;
    public event EventHandler<ToggledEventArgs> Toggled;

    public FlexButton()
    {
        try
        {
            InitializeComponent();
        }
        catch (Exception ex)
        {

        }

        //userChangedPadding = this.Padding.Equals(new Thickness(-1));

        TouchRecognizer.TouchDown += TouchDown;
        TouchRecognizer.TouchUp += TouchUp;
        SizeChanged += FlexButton_SizeChanged;
    }

    void FlexButton_SizeChanged(object sender, EventArgs e)
    {
        // HACK: Needs to be called to not make the Designer do stupid things
        //SetButtonMode();
        ColorIcon(ForegroundColor);
    }

    void TouchDown()
    {
        if (IsEnabled)
        {
            TouchedDown?.Invoke(this, EventArgs.Empty);
            TouchedDownCommand?.Execute(TouchedDownCommandParameter);

            Highlight(true);
        }
    }

    void TouchUp()
    {
        if (IsEnabled)
        {
            TouchedUp?.Invoke(this, EventArgs.Empty);
            TouchedUpCommand?.Execute(TouchedUpCommandParameter);
            Clicked?.Invoke(this, EventArgs.Empty);
            ClickedCommand?.Execute(ClickedCommandParameter);

            if (ToggleMode)
            {
                IsToggled = !IsToggled;
                Toggled?.Invoke(this, new ToggledEventArgs(IsToggled));

                Highlight(IsToggled);
            }
            else
            {
                Highlight(false);
            }
        }
    }

    void ColorIcon(Color color)
    {
        // Attach Color Overlay Effect
        if (ButtonIcon == null) return;
        ButtonIcon.Effects.Clear();

        if (IconTintEnabled)
        {
            ButtonIcon.Effects.Add(new ColorOverlayEffect { Color = color });
        }
    }

    void Highlight(bool isHighlighted)
    {
        if (Border == null || Container == null || ButtonText == null) return;
        if (isHighlighted)
        {
            Border.BackgroundColor = HighlightBorderColor;
            Container.BackgroundColor = HighlightBackgroundColor;
            ButtonText.TextColor = HighlightForegroundColor;
            ColorIcon(HighlightForegroundColor);
        }
        else
        {
            Border.BackgroundColor = BorderColor;
            Container.BackgroundColor = BackgroundColor;
            ButtonText.TextColor = ForegroundColor;
            ColorIcon(ForegroundColor);
        }
    }
}

} `

tomahutbui avatar Aug 22 '19 02:08 tomahutbui

Looks like the problem with Global Styles is, that they call OnPropertyChanged before the constructor has been called to initialize the object: https://stackoverflow.com/questions/46523431/xamarin-forms-bindableproperty-changed-before-constructor

robinmanuelthiel avatar Dec 21 '20 21:12 robinmanuelthiel

@tomahutbui I'm no longer working on that project. :/ But thanks anyway.

NickeManarin avatar Jan 08 '21 16:01 NickeManarin