jsmarcus / Iconize

Use icon fonts in your Xamarin.Forms application!
Other
204 stars 70 forks source link

Binding doesn't work #159

Open karak opened 5 years ago

karak commented 5 years ago

Binding Text property of IconButton doesn't work as expected(on iOS).

I created the following XAML, and dynamically changed the value to "fas-stop" from "fas-play", but saw "f...stop-icon" instead of "stop-icon".

I confirmed the text is right, and that a button initially set "fas-stop" works fine to conclude the binding mechanism of the component is simply broken.

<iconize:IconButton Text="{Binding PlayPauseButtonIcon}" Style="{StaticResource CommandbarButtonStyle}" Command="{Binding PlayPauseCommand}"/>

I tried to reproduce that bug by modifying your sample projects, only to fail to build just after checkout.

ADD: Application dependency:

charmosz commented 5 years ago

Same for me. It works on Android but not on iOS. Xamarin.Forms 4.0.0497661 Iconize.Forms 3.5.0.129

karak commented 5 years ago

I found some workaround. I removed "fas-" prefix when updating, and then this bugs magically disappeared.

Results:

karak commented 5 years ago

My solution is to use some attached property.

Creating this property class:

    public class FasIcon
    {
        private static readonly string SpecialInitialValue = DateTime.UtcNow.ToLongTimeString();
        private static bool isIOS => Device.RuntimePlatform != Device.iOS;
        private static bool IsInitial(object value) => object.ReferenceEquals(value, SpecialInitialValue);

        public static readonly BindableProperty IconProperty = BindableProperty.CreateAttached(
            "Icon", typeof(string), typeof(FasIcon), SpecialInitialValue,
            propertyChanged: OnPropertyChanged);

        private static void OnPropertyChanged(BindableObject bindable, object oldValue, object newValue)
        {
            if (isIOS || IsInitial(oldValue))
            {
                SetValue(bindable, newValue);
            }
            else
            {
                SetValue(bindable, RemovePrefix((string)newValue));
            }
        }

        private static void SetValue(BindableObject bindable, object value)
        {
            bindable.SetValue(IconButton.TextProperty, value);
        }

        private static string RemovePrefix(string value)
        {
            var pos = value.IndexOf('-');
            return pos > 0 ? value.Substring(pos + 1) : value;
        }

        public static string GetIcon(BindableObject element) => (string) element.GetValue(IconProperty);
        public static void SetIcon(BindableObject element, string value) => element.SetValue(IconProperty, value);
    }

Use it like this. I used it with triggers:

            <Style x:Key="PlayPauseButtonStyle" TargetType="iconize:IconButton" BasedOn="{StaticResource CommandbarButtonStyle}">
                <Style.Triggers>
                    <DataTrigger TargetType="iconize:IconButton" Binding="{Binding PlayPauseIconState}" Value="Pause">
                        <Setter Property="views:FasIcon.Icon" Value="fas-pause" />
                    </DataTrigger>
                    <DataTrigger TargetType="iconize:IconButton" Binding="{Binding PlayPauseIconState}" Value="Play">
                        <Setter Property="views:FasIcon.Icon" Value="fas-play" />
                    </DataTrigger>
                </Style.Triggers>
            </Style>