dotnet / maui

.NET MAUI is the .NET Multi-platform App UI, a framework for building native device applications spanning mobile, tablet, and desktop.
https://dot.net/maui
MIT License
22.24k stars 1.76k forks source link

Unable to bind `Glyph` property in `FontImageSource` of `ToolbarItem` #10186

Open maxkoshevoi opened 2 years ago

maxkoshevoi commented 2 years ago

Description

Original discussion: https://forums.xamarin.com/discussion/180852/why-glyph-property-in-fontimagesource-doesnt-bind Xamarin issue: https://github.com/xamarin/Xamarin.Forms/issues/12700

This code doesn't produce any errors, but Glyph is always empty:

<ContentPage.ToolbarItems>
    <ToolbarItem IconImageSource="{FontImageSource Glyph={Binding MyIcon}, FontFamily=MaterialDesignIcons}" />
</ContentPage.ToolbarItems>

Static binding works fine. Code above also works if it's added via Hot Reload (but after proper app reload icon is gone again)

image

Steps to Reproduce

  1. Add ToolbarItem to your page
  2. SpecifyIconImageSource to it as FontImageSource with binded Glyph

Link to public reproduction project repository

Remove the converter and start the app: https://github.com/maxkoshevoi/NureTimetable/blob/5f67cadbb78bc3c37df4985adf8c4ce3a936b4a0/NureTimetable/UI/Views/Timetable/TimetablePage.xaml#L20

Version with bug

6.0.486 (current)

Last version that worked well

Unknown/Other

Affected platforms

Android

Affected platform versions

All (tested on 13)

Did you find any workaround?

Thanks Saskia.

<ToolbarItem IconImageSource="{Binding MyIcon, Converter={converters:ToolbarIconValueConverter}}" />
public class ToolbarIconValueConverter : IValueConverter, IMarkupExtension
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return new FontImageSource
        {
            FontFamily = (OnPlatform<string>)Application.Current.Resources["MaterialFontFamily"],
            Glyph = (string)value
        };
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    public object ProvideValue(IServiceProvider serviceProvider)
    {
        return this;
    }
}

Relevant log output

No response

marcmognol commented 2 years ago

Hi @maxkoshevoi,

It works with the following code in my case:

        <ToolbarItem Command="{Binding CloseCommand}">
            <ToolbarItem.IconImageSource>
                <FontImageSource FontFamily="FontAwesomeSolid"
                                 Glyph="&#xf1d8;"
                                 Size="Medium" />
            </ToolbarItem.IconImageSource>
        </ToolbarItem>

Could you please test ?

Marc

marcmognol commented 2 years ago

My bad, you were talking about dynamic binding for glyph. Sorry

cdzhoubin commented 2 years ago

https://www.reddit.com/r/xamarindevelopers/comments/c0jnt5/fontimagesource_glyph_icon_with_data_binding_only/

ghost commented 2 years ago

We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process.

PureWeen commented 2 years ago

Hi @maxkoshevoi,

It works with the following code in my case:

        <ToolbarItem Command="{Binding CloseCommand}">
            <ToolbarItem.IconImageSource>
                <FontImageSource FontFamily="FontAwesomeSolid"
                                 Glyph="&#xf1d8;"
                                 Size="Medium" />
            </ToolbarItem.IconImageSource>
        </ToolbarItem>

Could you please test ?

Marc

Does this work?

        <ToolbarItem Command="{Binding CloseCommand}">
            <ToolbarItem.IconImageSource>
                <FontImageSource FontFamily="FontAwesomeSolid"
                                 Glyph="{Binding MyIcon}"
                                 Size="Medium" />
            </ToolbarItem.IconImageSource>
        </ToolbarItem>
maxkoshevoi commented 2 years ago

Does this work?

        <ToolbarItem Command="{Binding CloseCommand}">
            <ToolbarItem.IconImageSource>
                <FontImageSource FontFamily="FontAwesomeSolid"
                                 Glyph="{Binding MyIcon}"
                                 Size="Medium" />
            </ToolbarItem.IconImageSource>
        </ToolbarItem>

No, it doesn't, still getting an empty icon (same as in Xamarin)

maxkoshevoi commented 2 years ago

reddit.com/r/xamarindevelopers/comments/c0jnt5/fontimagesource_glyph_icon_with_data_binding_only

This doesn't solve the issue of dynamic binding. I already use glyphs formatted like \U000f14ef. Also tried &#x000f0232 just in case, and it doesn't work for both formats.

MichaelAzzer commented 2 years ago

The problem was, that you have to escape Unicode codes differently in csharp and XAML.

XAML : "\" is equivalent to C# "\uf308"

Changing the escape format in the ValueConverter to the correct C# format yields correct results..

https://www.reddit.com/r/xamarindevelopers/comments/c0jnt5/fontimagesource_glyph_icon_with_data_binding_only/

maxkoshevoi commented 2 years ago

@MichaelAzzer as I said in the comment above, I do escape glyphs correctly, and dynamic binding still doesn't work

MichaelAzzer commented 2 years ago

It's working for me in .net maui with dynamic binding

maxkoshevoi commented 2 years ago
MichaelAzzer commented 2 years ago

Yes it's working without converter, you can path glyph as a string from your data model

Get Outlook for Androidhttps://aka.ms/AAb9ysg


From: Maksym Koshovyi @.> Sent: Tuesday, November 8, 2022 3:42:40 PM To: dotnet/maui @.> Cc: Michael Azzer @.>; Mention @.> Subject: Re: [dotnet/maui] Unable to bind Glyph property in FontImageSource of ToolbarItem (Issue #10186)

— Reply to this email directly, view it on GitHubhttps://github.com/dotnet/maui/issues/10186#issuecomment-1307242070, or unsubscribehttps://github.com/notifications/unsubscribe-auth/ADRJJRXM5ERC3CDHQQ2BDATWHJKFBANCNFSM6AAAAAAQPE72UI. You are receiving this because you were mentioned.Message ID: @.***>

maxkoshevoi commented 2 years ago

Yes it's working without converter

Could you please share a sample project? It doesn't work on my side as I've described here: https://github.com/dotnet/maui/issues/10186#issuecomment-1268835353

MichaelAzzer commented 2 years ago

OK I will prepare small sample

Get Outlook for Androidhttps://aka.ms/AAb9ysg


From: Maksym Koshovyi @.> Sent: Wednesday, November 9, 2022 10:32:56 AM To: dotnet/maui @.> Cc: Michael Azzer @.>; Mention @.> Subject: Re: [dotnet/maui] Unable to bind Glyph property in FontImageSource of ToolbarItem (Issue #10186)

Yes it's working without converter

Could you please share a sample project? It doesn't work on my side as I've described here: #10186 (comment)https://github.com/dotnet/maui/issues/10186#issuecomment-1268835353

— Reply to this email directly, view it on GitHubhttps://github.com/dotnet/maui/issues/10186#issuecomment-1308391797, or unsubscribehttps://github.com/notifications/unsubscribe-auth/ADRJJRWYWAM3APJ7D2AHCS3WHNOTRANCNFSM6AAAAAAQPE72UI. You are receiving this because you were mentioned.Message ID: @.***>

MichaelAzzer commented 2 years ago

Sorry I tested it with regular image not with toolbar, but it’s not working with toolbar I’m sorry for that

Sent from Mailhttps://go.microsoft.com/fwlink/?LinkId=550986 for Windows

From: Maksym @.> Sent: Wednesday, November 9, 2022 10:32 AM To: @.> Cc: Michael @.>; @.> Subject: Re: [dotnet/maui] Unable to bind Glyph property in FontImageSource of ToolbarItem (Issue #10186)

Yes it's working without converter

Could you please share a sample project? It doesn't work on my side as I've described here: #10186 (comment)https://github.com/dotnet/maui/issues/10186#issuecomment-1268835353

— Reply to this email directly, view it on GitHubhttps://github.com/dotnet/maui/issues/10186#issuecomment-1308391797, or unsubscribehttps://github.com/notifications/unsubscribe-auth/ADRJJRWYWAM3APJ7D2AHCS3WHNOTRANCNFSM6AAAAAAQPE72UI. You are receiving this because you were mentioned.Message ID: @.***>

pawelFelcyn commented 1 year ago

Hi, if you have not found any solution yet, I have something you probably will be interested in. I faced the same issue today in my work and came up with something like this. Binding of Glypf does not work even if you write your own function, which listens to PropertyChanged event in your view model. But there is only one solution that worked for me. First create class like this:

public class GlyphConverter : IMultiValueConverter, IMarkupExtension
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (values is null || values.Length == 0 || !(values[0] is string))
        {
            //You can handle this case in some other way, but you have to return object of type ImageSource
            return new FontImageSource();
        }

        return new FontImageSource()
        {
            Glyph = values[0] as string,
            FontFamily = "FA-S" //here you have to write alias of your font family (specified in MauiProgram.cs)
        };
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        if (!(value is FontImageSource fontImageSource))
        {
            throw new ArgumentException(nameof(value));
        }

        return new object[] { fontImageSource.Glyph };
    }

    public object ProvideValue(IServiceProvider serviceProvider)
    {
        return this;
    }
}

This is multi value converter. Now your xaml file should look something like this

<Shell.ToolbarItems>
    <ToolbarItem>
      <ToolbarItem.IconImageSource>
        <MultiBinding Converter="{local:GlyphConverter}">
          <Binding Path="Glyph"/>
        </MultiBinding>
      </ToolbarItem.IconImageSource>
    </ToolbarItem>
  </Shell.ToolbarItems>

Works in .NET MAUI. I also tried binding the whole IconImageSource (not just glyph) and use IValueConverter, but it was throwing some exception, which I could not catch.

maxkoshevoi commented 1 year ago

@pawelFelcyn Hi, I'm using ToolbarIconValueConverter from the workaround provided in the issue description. It's similar to what you did

ahmadun commented 1 year ago

I have solved same similiar dynamic binding with converter, I am using MAUItollkit

namespace TroubleCallApp.Converter
{
    internal class FlagToIcon : BaseConverter<object, string>
    {
        public override string DefaultConvertReturnValue { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
        public override object DefaultConvertBackReturnValue { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }

        public override object ConvertBackTo(string value, CultureInfo culture)
        {
            throw new NotImplementedException();
        }

        public override string ConvertFrom(object value, CultureInfo culture)
        {
            return value switch
            {
                0 => "\uf254;",
                _ => "\uf254;"
            };
        }
    }
}
 <Image>
                                                <Image.Source>
                                                    <FontImageSource Size="10" FontFamily="FAS" Glyph="{Binding flag,Converter={StaticResource FlagToIcon}}" Color="Black"/>
                                                </Image.Source>
                                            </Image>
1100241 commented 1 year ago

Hi,

I don't know if was solved yet, but here is a nice solution for a direct binding:

test: escaped html:  your string: f100

code: string myString = "f100"; var chars = new char[] { (char)Convert.ToInt32(myString, 16) }; return new string(chars);

https://stackoverflow.com/questions/64704891/how-to-construct-a-unicode-glyph-key-in-c-sharp

cmhofmeister commented 1 year ago

Hi, I am dealing with this issue and tried a minimal example on the newly released dot net 8 using a FontImageSource. I can't get any of the proposed workarounds to work. I attached the example.

You will note that the main page displays a font icon and that the INotifyPropertyChanged is firing as the icon changes. In the toolbar items I have several approaches. I hope I'm not missing something obvious, but that is always a possibility...

ToolbarItem.zip

Zhanglirong-Winnie commented 11 months ago

Verified this issue with Visual Studio Enterprise 17.9.0 Preview 1.0. Can repro on android platform.

damusthe commented 6 months ago

I've the same issue in .Net 8 and CommunityToolkit.Maui v9.0.0

It's not working with binding and a converter :

<!-- Google Material Icons https://fonts.google.com/icons -->
<x:String x:Key="MaterialIcon_Location_On">&#xe0c8;</x:String>
<x:String x:Key="MaterialIcon_Location_Off">&#xe0c7;</x:String>

xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"

<Page.Resources>
    <toolkit:BoolToObjectConverter x:Key="BoolToGeoLocationStatusConverter" TrueObject="{StaticResource MaterialIcon_Location_On}" FalseObject="{StaticResource MaterialIcon_Location_Off}"/>
</Page.Resources>

<!-- Toolbar Items -->
<ContentPage.ToolbarItems>
    <ToolbarItem Text="{Binding IsGeoLocationAcquiredStringStatus}">
        <ToolbarItem.IconImageSource>
            <FontImageSource FontFamily="MaterialIconsOutlined-Regular" Glyph="{Binding IsGeoLocationAcquired, Mode=OneWay, Converter={StaticResource BoolToGeoLocationStatusConverter}}" FontAutoScalingEnabled="True" Color="Black"/>
        </ToolbarItem.IconImageSource>
    </ToolbarItem>
</ContentPage.ToolbarItems>

It's working with static resource :

<!-- Toolbar Items -->
<ContentPage.ToolbarItems>
    <ToolbarItem Text="{Binding IsGeoLocationAcquiredStringStatus}">
        <ToolbarItem.IconImageSource>
            <FontImageSource FontFamily="MaterialIconsOutlined-Regular" Glyph="{StaticResource MaterialIcon_Location_On}" FontAutoScalingEnabled="True" Color="Black"/>
        </ToolbarItem.IconImageSource>
    </ToolbarItem>
</ContentPage.ToolbarItems>
damusthe commented 6 months ago

Following my previous post, an obvious dirty workaround that's workings using code in a ViewModel. The main principle is to change ToolbarItems[0] item with a ToolbarItem depending on a property.

public IList<ToolbarItem> ToolbarItems;   // Set it at startup in the page code behind
private ToolbarItem _toolbarItemGeoLocationIconStatusOFF;
private ToolbarItem _toolbarItemGeoLocationIconStatusON;

// Create dynamically toolbar Item Geo Location Icon Status ON
// Woraround of Binding that's not working
FontImageSource fontImageSourceGeoLocationIconStatusON = new FontImageSource()
{
    FontFamily = "MaterialIconsOutlined-Regular",
    Glyph = "\ue0c8",
    FontAutoScalingEnabled = true,
    Color = Colors.Black
};
_toolbarItemGeoLocationIconStatusON = new ToolbarItem
{
    Text = "",
    IconImageSource = fontImageSourceGeoLocationIconStatusON
};

// Create dynamically toolbar Item Geo Location Icon Status OFF
// Woraround of Binding that's not working
FontImageSource fontImageSourceGeoLocationIconStatusOFF = new FontImageSource()
{
    FontFamily = "MaterialIconsOutlined-Regular",
    Glyph = "\ue0c7",
    FontAutoScalingEnabled = true,
    Color = Colors.Black
};
_toolbarItemGeoLocationIconStatusOFF = new ToolbarItem
{
    Text = "",
    IconImageSource = fontImageSourceGeoLocationIconStatusOFF
};

...

if (ToolbarItems.Count == 0)
{
    // Set Geo Location Indicator default Status
    ToolbarItems.Add(_toolbarItemGeoLocationIconStatusOFF);
}

if (IsGeoLocationAcquired)
{
    ToolbarItems[0] = _toolbarItemGeoLocationIconStatusON;
}
else
{
    ToolbarItems[0] = _toolbarItemGeoLocationIconStatusOFF;
}
pictos commented 6 months ago

If you set it using C# it will work nicely. For some reason when using xaml the IconImageSource isn't notified about the Glyph proeprty changed.

<ContentPage.ToolbarItems>
    <ToolbarItem x:Name="toolbarItem3" />
</ContentPage.ToolbarItems>
.ctor()
{
    InitializeComponent();
    var fontImageSource = new FontImageSource
    {
        FontFamily = "MaterialFont"
    };
    fontImageSource.SetBinding(FontImageSource.GlyphProperty, new Binding()
    {
        Source = this.BindingContext,
        Path = "Agenda",
    });

    this.toolbarItem3.IconImageSource = fontImageSource;
}