Skip to content
This repository has been archived by the owner on May 1, 2024. It is now read-only.

[Bug] Font Icon Disappears when Minimizing Window in UWP #8293

Open
djrpascu opened this issue Oct 30, 2019 · 28 comments
Open

[Bug] Font Icon Disappears when Minimizing Window in UWP #8293

djrpascu opened this issue Oct 30, 2019 · 28 comments

Comments

@djrpascu
Copy link

Description

In a XF UWP app, the font icon seems to disappear after minimizing the window a few times. Seems to disappear after minimizing and opening the window 4 times for me. But I've observed it to be inconsistent at times, sometimes after minimizing 2 times, others more than 10 times.

Steps to Reproduce

  1. Run simple reproduction example below.
  2. Minimize and open window on UWP until icon disappears.

Expected Behavior

Icon remains in view.

Actual Behavior

Icon disappears.

Basic Information

  • Version with issue: XF 4.3.0.947036
  • Last known good version: N/A
  • IDE: VS Enterprise 2019
  • Platform Target Frameworks:
    • UWP: 16299 (also tested on 17763 with same results)
  • Affected Devices: UWP

Screenshots

Reproduction Link

XFFontIconBug Repo

@djrpascu djrpascu added s/unverified New report that has yet to be verified t/bug 🐛 labels Oct 30, 2019
@pauldipietro pauldipietro added this to New in Triage Oct 30, 2019
@hartez
Copy link
Contributor

hartez commented Oct 30, 2019

@djrpascu I've been minimizing and restoring this window for several minutes now, and the icon is still there. I think we might be missing some details.

Do you have multiple monitors?
Are you reproducing this in desktop mode or tablet mode?

@hartez hartez added the s/needs-info ❓ A question has been asked that requires an answer before work can continue on this issue. label Oct 30, 2019
@hartez hartez moved this from New to Needs Info in Triage Oct 30, 2019
@djrpascu
Copy link
Author

@hartez hmm... i have a laptop on a dock and 2 monitors (24"), so 3 screens all together. Not sure about desktop/tablet mode. I just try it from within VS under "Local Machine", Debug, x86. I also try running the version that gets installed on my machine after debugging. Let me know if there is any further detail I can provide. I'm going to have a co-worker try it on his machine as well.

@hartez
Copy link
Contributor

hartez commented Oct 30, 2019

I ask about the multiple monitors because we've had a couple of UWP bugs in the past that only show up when running on a secondary monitor.

@hartez
Copy link
Contributor

hartez commented Oct 30, 2019

I wonder if you're encountering this problem: #7505

@hartez hartez added p/UWP and removed s/needs-info ❓ A question has been asked that requires an answer before work can continue on this issue. s/unverified New report that has yet to be verified labels Oct 30, 2019
@hartez
Copy link
Contributor

hartez commented Oct 30, 2019

Possible duplicate of #7505 - once that's merged, we should get a build to @djrpascu to test.

@hartez hartez added this to To do in UWP Ready For Work via automation Oct 30, 2019
@hartez hartez removed this from Needs Info in Triage Oct 30, 2019
@djrpascu
Copy link
Author

@hartez thanks, I took my laptop off the dock and tried with just that single monitor and was still able to reproduce the issue.. still waiting on co-worker results.. send me the build, glad to test it out.. thanks much!

@djrpascu
Copy link
Author

just wanted to follow-up on this... i'm still seeing the issue.. tried switching to UWP version 1903 build 18362, but still same.. my co-worker doesn't experience it on his machine.. can get difference in build specs if needed..

@leoholcman
Copy link

Same behaviour here... In my case it's happening after around 10 mins.

@Samit-BTE
Copy link

I can constantly able to reproduce it by minimizing an application and activate another application.

Any update or workaround for this issue will be helpful.

@BurkusCat
Copy link
Contributor

I am not experiencing the issue but some of my colleagues are. It happens when the app is minimised for them.

A machine where it was failing was running at 1920x1080 @ 125% DPI scale on Windows 1909 OS Build 18363.900. My machine when I tried switching to those display settings (Windows 1903 OS Build 18632.900) did not have the issue.

@samhouts samhouts added this to the 5.0.0 milestone Aug 13, 2020
@samhouts samhouts added this to To do in vNext+1 (5.0.0) Aug 14, 2020
@rueldognidon
Copy link

It seems that the fix was merged here: #10161, Analyzing the fix, It seems that it only applies to Toolbar Items and ImageRenderers. So if you use a button with ImageSource, this seems to be not covered by the fix. I'm still experiencing this on my buttons with fonticonsource.

@randyrenwick
Copy link

randyrenwick commented Sep 23, 2020

It seems that the fix was merged here: #10161, Analyzing the fix, It seems that it only applies to Toolbar Items and ImageRenderers. So if you use a button with ImageSource, this seems to be not covered by the fix. I'm still experiencing this on my buttons with fonticonsource.

I agree with @rueldognidon regarding PR 10161. Same fix needs to be applied to other controls. Button, ImageButton and Image all still have this problem.

My notes:

Tested with:

  • Visual Studio 2019 (V16.7.4)
  • XF Version with issue: 4.8.0.1451
  • Platform Target Frameworks: UWP: 18362

I tested with new approach of using ExportFont and including font in main project but still had same problems as old way (old way = including font in UWP project and mapping it through a static resource).

New test repo with this new approach is here for reference:
https://github.com/randyrenwick/XF.UWP.FontImageSourceDisappearSample

Not sure if this has every worked in any version for UWP. For iOS it works fine.

Consistently happens for me if I minimize the window for 5 seconds and then restore it. Video demo here:
https://github.com/randyrenwick/XF.UWP.FontImageSourceDisappearSample/raw/master/UWP%20FontImageSource%20Minimize%20Disappearing%20Images.mp4

Note the same problem happens if I start the app and then go to the Windows Lock screen and then sign in again.

Once images disappear I can't get them back without restarting the app.

I did my testing on a laptop without any external monitors connected. 1920 x 1080 resolution. 125% scale. Testing in Desktop mode.

Happens for these controls: Button, ImageButton, Image

@randyrenwick
Copy link

Confirmed this is still reproduceable in 5.0.0.1487-pre1 as well.

@holecekp
Copy link

I can also confirm that this bug is in XF 4.8 SR 1. This is strange because we have discussed it in #8783 and it seemed to disappear suddenly in XF 4.8 on my computer. But now it is back. I had to reinstall Windows on that computer some time ago so I am not sure if the return of the bug was caused by some changes between 4.8 and 4.8 SR 1, or if it is caused by the OS reinstall.

@samhouts samhouts removed this from the 5.0.0 milestone Nov 2, 2020
@keithmullins
Copy link

We are also encountering this issue when minimising/maximising our UWP application. As others have pointed out, the application needs to be restarted before you can see the icons again. This is a major issue for us!!

@Only-Xam
Copy link

Only-Xam commented Feb 9, 2021

Issue persist on UWP and noticed that it is only happening with Image Button, with Label it is working fine.

`            <Label
                FontFamily="FontAwesome"
                FontSize="50"
                HeightRequest="100"
                HorizontalTextAlignment="Center"
                Text="{x:Static localfonts:IconFont.Anchor}"
                TextColor="Yellow"
                VerticalTextAlignment="Center"
                WidthRequest="100" />
            <Button
                BackgroundColor="#1d1d1d"
                HeightRequest="100"
                ImageSource="{StaticResource AnchorIcon}"
                WidthRequest="100" />`

MichielDG added a commit to MichielDG/Xamarin.Forms that referenced this issue Mar 9, 2021
…mes again the image source should be reupdated as uwp replaces images with transparent images onResume
MichielDG added a commit to MichielDG/Xamarin.Forms that referenced this issue Mar 9, 2021
…ng Window in UWP

When the uwp app suspends and resumes again the image source should be reupdated as uwp replaces images with transparent images onResume
@randyrenwick
Copy link

This commit will fix ImageButton control 👍

But this problem also happens for Button and Image controls. Can the same fix be applied to them as well please.

@MichielDG
Copy link

This commit will fix ImageButton control 👍

But this problem also happens for Button and Image controls. Can the same fix be applied to them as well please.

This fix is already applied to the imagerenderer. See pull request #10161

@holecekp
Copy link

The problem still persists for ImageButton. Also the latest Xamarin.Forms 5.0 SR 5 contains this bug. The app does not have to be minimized. The FontIcon can disappear also when the app is opened in the foreground for a longer time without user interaction.

@vladimirsakic
Copy link

vladimirsakic commented Oct 20, 2021

How can we resolve this, it's such basic problem going on for 2 years, we can't have apps with despairing buttons

@djrpascu
Copy link
Author

@holecekp @vladimirsakic Unfortunately, I'm still seeing the issue too still in XF 5.0.0.2012. I tried the fixes proposed by @MichielDG as well, but icons still disappeared. I ended up creating png files for all my icons. I have a feeling, most all of these issues are getting put on back burner for .NET MAUI, hopefully we won't run into issues like these then?

@vladimirsakic
Copy link

@djrpascu Thanks a lot, replacing icons with pngs would also work for me, that solution didn't cross my mind.
By the way, how do you apply those proposed fixes, by cloning whole Xamarin.Forms project instead of getting it from nuget and replacing certain files? Is there a way to add only files that needs to be changed to my solution (ImageRenderer.cs) and still use Xamarin.Forms from nuget?

tonyhallett added a commit to tonyhallett/Xamarin.Forms that referenced this issue Jan 12, 2022
xamarin#8606

This resolves issue above although I think for UWP it may be better to just use a TextBlock.  Renderers affected by xamarin#8293 and xamarin#14323 can check the type of ImageSource and if FontImageSource can render a TextBlock.

I say may as I have not throughly tested my idea but it appears to work for a ButtonRenderer with a slight adjustment of existing code.

I will add code to mentioned issues shortly.

```
internal static class FontImageSourceExtensions
    {
		public static TextBlock ToTextBlock(this FontImageSource fontImageSource)
        {
			var fontFamily = fontImageSource.FontFamily;
			var iconColor = fontImageSource.Color != Color.Default ? fontImageSource.Color : Color.White;
			var textBlock = new TextBlock() { Text = fontImageSource.Glyph, Foreground = iconColor.ToBrush() };
			var font = Font.OfSize(fontFamily, fontImageSource.Size);
			textBlock.ApplyFont(font);
			return textBlock;
		}
    }
```
@tonyhallett
Copy link
Contributor

tonyhallett commented Jan 13, 2022

After trying the following.

Creating a StreamImageSource that drew icons with skia.

using System;
using System.IO;
using System.Threading.Tasks;
using SkiaSharp;
using SkiaSharp.Views.Forms;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace My.MarkupExtensions
{
    public static class FontIconBitmapStreamProvider
    {
        public static Stream Create(SKTypeface typeface, string iconId, int size = 24, Color? iconColor = null)
        {
            Color color = iconColor.HasValue ? iconColor.Value : Color.Default;
            var stream = new MemoryStream();

            using (SKPaint textPaint = new SKPaint { TextSize = size, Color = color.ToSKColor(), Typeface = typeface, IsAntialias = true, FilterQuality = SKFilterQuality.High, SubpixelText = true })
            {
                SKRect bounds = new SKRect();

                textPaint.MeasureText(iconId, ref bounds);

                var iconBitmap = new SKBitmap(size, size);

                using (SKCanvas canvas = new SKCanvas(iconBitmap))
                {
                    canvas.Clear();
                    textPaint.Style = SKPaintStyle.Fill;

                    var midPointY = (-bounds.Top - (-bounds.Bottom)) / 2;
                    var y = -bounds.Top + (size / 2 - midPointY);
                    canvas.DrawText(iconId, 0, y, textPaint);
                }
                //https://github.com/mono/SkiaSharp/issues/320#issuecomment-310805723 bmp not supported !
                bool success = iconBitmap.Encode(stream, SKEncodedImageFormat.Png, 100);
            }

            stream.Position = 0;
            return stream;

        }
    }

    public abstract class FontIconStreamImageSourceExtension : BindableObject,IMarkupExtension<StreamImageSource>
    {
        public static readonly BindableProperty GlyphProperty = BindableProperty.Create(nameof(Glyph), typeof(string), typeof(FontIconStreamImageSourceExtension));
        public static readonly BindableProperty ColourProperty = BindableProperty.Create(nameof(Colour), typeof(Color), typeof(FontIconStreamImageSourceExtension));
        public static readonly BindableProperty SizeProperty = BindableProperty.Create(nameof(Size), typeof(int), typeof(FontIconStreamImageSourceExtension),30);
        public int Size { 
            get => (int)GetValue(SizeProperty);
            set => SetValue(SizeProperty, value); 
        }
        public string Glyph { 
            get => (string)GetValue(GlyphProperty);
            set => SetValue(GlyphProperty, value);
        }
        public Color Colour { 
            get => (Color)GetValue(ColourProperty);
            set => SetValue(ColourProperty, value);
        }

        protected abstract SKTypeface GetTypeface();

        public StreamImageSource ProvideValue(IServiceProvider serviceProvider)
        {
            return new StreamImageSource() { Stream = (ct) => Task.FromResult<Stream>(FontIconBitmapStreamProvider.Create(GetTypeface(),Glyph, Size, Colour)) };
        }
        object IMarkupExtension.ProvideValue(IServiceProvider serviceProvider)
        {
            return ProvideValue(serviceProvider);
        }
    }
}

Create implementation to provide the typeface - e.g

    public static class SKTypefaceExtensions
    {
        public static SKTypeface SKTypefaceFromResource(this Assembly assembly,string resource)
        {
            SKTypeface result;

            var stream = assembly.GetManifestResourceStream(resource);
            if (stream == null)
                return null;

            result = SKTypeface.FromStream(stream);
            return result;
        }
    }

    public class AssemblyResourceFontIconStreamImageSourceExtension : FontIconStreamImageSourceExtension
    {
        private SKTypeface skTypeface;
        public string Resource { get; set; }
        protected override SKTypeface GetTypeface()
        {
            if (skTypeface == null)
            {
                skTypeface = this.GetType().Assembly.SKTypefaceFromResource(Resource);
            }
            return skTypeface;
        }
    }

Advantages
does not disappear
can be colour bound so better than using a png resource.
is centered correctly - see https://github.com/xamarin/Xamarin.Forms/pull/15047/files
Disadvantages
Blurry

Creating a FontImageSourceHandler and drawing to BitmapImage using similar code to existing ( with the alignment correction )
Same advantages and disadvantages as before

Creating a custom renderer, checking the ImageSource type and if FontImageSource then create a TextBlock instead !
Same advantages and is anti-aliased. ( There may be some disadvantages that I have yet to encounter )

I can paste the code in here if anyone wants to go down that route as it is a pain matching the existing code due to internals. I only did ButtonRenderer as it occurred to me that it may be possible to do with an effect.

The effect below is for Button and ImageButton. For an image you cannot hack in this manner and I instead I just use a Label ( which does not suffer from the alignment issue.)

The code just converts the FontImageSource to a TextBlock and replaces the Image that is created by the renderer.


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using Xamarin.Forms;
using Xamarin.Forms.Platform.UWP;

namespace Effects
{
    internal static class FontImageSourceExtensions
    {
        public static Windows.UI.Xaml.Controls.TextBlock ToTextBlock(this FontImageSource fontImageSource)
        {
            var fontFamily = fontImageSource.FontFamily;
            var iconColor = fontImageSource.Color != Color.Default ? fontImageSource.Color : Color.White;
            var textBlock = new Windows.UI.Xaml.Controls.TextBlock() { Text = fontImageSource.Glyph, Foreground = iconColor.ToBrush() };
            var font = Font.OfSize(fontFamily, fontImageSource.Size);
            textBlock.ApplyFont(font);
            return textBlock;
        }
    }

    internal class FixButtonIconEffect : PlatformEffect
    {
        private static readonly List<IFontImageSourceHacker> hackers = new List<IFontImageSourceHacker> { new ButtonFontImageSourceHacker(), new ImageButtonFontImageSourceHacker()};
        
        private interface IFontImageSourceHacker
        {
            IEnumerable<BindableProperty> PropertiesUpdatingContent { get; }
            bool CanHack(Element element);
            ImageSource GetImageSource(Element element);
            void Hack(Element element, FontImageSource fontImageSource, FormsButton formsButton);
        }

        public abstract class FontImageSourceHackerBase<T> : IFontImageSourceHacker where T:Element
        {
            public abstract IEnumerable<BindableProperty> PropertiesUpdatingContent { get; }

            public bool CanHack(Element element)
            {
                return element.GetType() == typeof(T);
            }

            public ImageSource GetImageSource(Element element)
            {
                return DoGetImageSource(element as T);
            }
            protected abstract ImageSource DoGetImageSource(T element);

            protected abstract void DoHack(T element, FontImageSource fontImageSource,FormsButton formsButton);

            public void Hack(Element element, FontImageSource fontImageSource, FormsButton formsButton)
            {
                DoHack(element as T,fontImageSource,formsButton);
            }

        }

        public class ButtonFontImageSourceHacker : FontImageSourceHackerBase<Button>
        {
            private List<BindableProperty> propertiesUpdatingContent = new List<BindableProperty> { Button.ImageSourceProperty, Button.TextProperty, Button.TextTransformProperty };
            public override IEnumerable<BindableProperty> PropertiesUpdatingContent => propertiesUpdatingContent;

            protected override ImageSource DoGetImageSource(Button element)
            {
                return element.ImageSource;
            }
            protected override void DoHack(Button buttonElement,FontImageSource fontImageSource, FormsButton formsButton)
            {
                var iconTextBlock = fontImageSource.ToTextBlock(); ;
                if (buttonElement.Text == null)
                {
                    formsButton.Content = iconTextBlock;
                }
                else
                {
                    var stackPanel = formsButton.Content as Windows.UI.Xaml.Controls.StackPanel;
                    var children = stackPanel.Children;
                    var textBlockIndex = children[0] is Windows.UI.Xaml.Controls.TextBlock ? 0 : 1;
                    var iconIndex = Math.Abs(textBlockIndex - 1);
                    var iconMargin = ((Windows.UI.Xaml.FrameworkElement)children[iconIndex]).Margin;
                    iconTextBlock.Margin = iconMargin;
                    children.RemoveAt(iconIndex);
                    children.Insert(iconIndex, iconTextBlock);
                }
            }
        }

        public class ImageButtonFontImageSourceHacker : FontImageSourceHackerBase<ImageButton>
        {
            private List<BindableProperty> propertiesUpdatingContent = new List<BindableProperty> { ImageButton.SourceProperty};
            public override IEnumerable<BindableProperty> PropertiesUpdatingContent => propertiesUpdatingContent;

            protected override ImageSource DoGetImageSource(ImageButton element)
            {
                return element.Source;
            }

            protected override void DoHack(ImageButton imageButton, FontImageSource fontImageSource, FormsButton formsButton)
            {
                formsButton.Content = fontImageSource.ToTextBlock();
            }
        }

        private IFontImageSourceHacker hacker;

        protected override void OnElementPropertyChanged(PropertyChangedEventArgs args)
        {
            if(hacker != null && hacker.PropertiesUpdatingContent.Any(bp => bp.PropertyName == args.PropertyName))
            {
                TryHack();
            }
            
            base.OnElementPropertyChanged(args);
        }

        protected override void OnAttached()
        {
            hacker = hackers.FirstOrDefault(hacker => hacker.CanHack(Element));
            TryHack();
        }

        private void TryHack()
        {
            if (hacker != null && hacker.GetImageSource(Element) is FontImageSource fontImageSource)
            {
                hacker.Hack(Element,fontImageSource, Control as FormsButton);
            }
        }

        protected override void OnDetached() { }
    }
}

The behaviour is a little bit different for ImageButton. For

           <Button HorizontalOptions="Start">
                <Button.ImageSource>
                    <FontImageSource Color="{AppThemeBinding Light=Black,Dark=White}" Size="24" FontFamily="GoogleFontIcons" Glyph="{x:Static fonthelpers:GoogleFontIcons.AddCircleOutline}"/>
                </Button.ImageSource>
                <Button.Effects>
                    <effects:FixButtonIconEffect/>
                </Button.Effects>
            </Button>
          
            <Button HorizontalOptions="Start" Text="Text">
                <Button.ImageSource>
                    <FontImageSource Color="{AppThemeBinding Light=Black,Dark=White}" Size="24" FontFamily="GoogleFontIcons" Glyph="{x:Static fonthelpers:GoogleFontIcons.Alarm}"/>
                </Button.ImageSource>
                <Button.Effects>
                    <effects:FixButtonIconEffect/>
                </Button.Effects>
            </Button>
          
            <Button HorizontalOptions="Start">
                <Button.ImageSource>
                    <FontImageSource Color="Red" Size="30" FontFamily="GoogleFontIcons" Glyph="{x:Static fonthelpers:GoogleFontIcons.BackHand}"/>
                </Button.ImageSource>
                <Button.Effects>
                    <effects:FixButtonIconEffect/>
                </Button.Effects>
            </Button>
            
            <ImageButton HorizontalOptions="Start">
                <ImageButton.Source>
                    <FontImageSource Color="Red" Size="30" FontFamily="GoogleFontIcons" Glyph="{x:Static fonthelpers:GoogleFontIcons.BackHand}"/>
                </ImageButton.Source>
                <ImageButton.Effects>
                    <effects:FixButtonIconEffect/>
                </ImageButton.Effects>
            </ImageButton>

            <ImageButton HorizontalOptions="Start">
                <ImageButton.Source>
                    <FontImageSource Color="Red" Size="10" FontFamily="GoogleFontIcons" Glyph="{x:Static fonthelpers:GoogleFontIcons.BackHand}"/>
                </ImageButton.Source>
                <ImageButton.Effects>
                    <effects:FixButtonIconEffect/>
                </ImageButton.Effects>
            </ImageButton>

            <ImageButton HorizontalOptions="Start" HeightRequest="40" WidthRequest="40">
                <ImageButton.Source>
                    <FontImageSource Color="Red" Size="30" FontFamily="GoogleFontIcons" Glyph="{x:Static fonthelpers:GoogleFontIcons.BackHand}"/>
                </ImageButton.Source>
                <ImageButton.Effects>
                    <effects:FixButtonIconEffect/>
                </ImageButton.Effects>
            </ImageButton>

            <ImageButton HorizontalOptions="Start" HeightRequest="40" WidthRequest="40">
                <ImageButton.Source>
                    <FontImageSource Color="Red" Size="10" FontFamily="GoogleFontIcons" Glyph="{x:Static fonthelpers:GoogleFontIcons.BackHand}"/>
                </ImageButton.Source>
                <ImageButton.Effects>
                    <effects:FixButtonIconEffect/>
                </ImageButton.Effects>
            </ImageButton>
           
          <!-- without effect -->
          
          <ImageButton HorizontalOptions="Start">
                <ImageButton.Source>
                    <FontImageSource Color="Red" Size="30" FontFamily="GoogleFontIcons" Glyph="{x:Static fonthelpers:GoogleFontIcons.BackHand}"/>
                </ImageButton.Source>
            </ImageButton>

            <ImageButton HorizontalOptions="Start">
                <ImageButton.Source>
                    <FontImageSource Color="Red" Size="10" FontFamily="GoogleFontIcons" Glyph="{x:Static fonthelpers:GoogleFontIcons.BackHand}"/>
                </ImageButton.Source>
            </ImageButton>

            <ImageButton HorizontalOptions="Start" HeightRequest="40" WidthRequest="40">
                <ImageButton.Source>
                    <FontImageSource Color="Red" Size="30" FontFamily="GoogleFontIcons" Glyph="{x:Static fonthelpers:GoogleFontIcons.BackHand}"/>
                </ImageButton.Source>
            </ImageButton>

            <ImageButton HorizontalOptions="Start" HeightRequest="40" WidthRequest="40">
                <ImageButton.Source>
                    <FontImageSource Color="Red" Size="10" FontFamily="GoogleFontIcons" Glyph="{x:Static fonthelpers:GoogleFontIcons.BackHand}"/>
                </ImageButton.Source>
            </ImageButton>

image

and of course after minimizing

image

@tonyhallett
Copy link
Contributor

tonyhallett commented Jan 13, 2022

You can add your Button or ImageButton inside this to have the effect applied

[XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class IconButton : ContentView
    {
        public IconButton()
        {
            InitializeComponent();
        }

        protected override void OnChildAdded(Element child)
        {
            base.OnChildAdded(child);
            if (!(child is ContentPresenter))
            {
                Content.Effects.Add(new FixButtonIconEffect());
            }
        }
    }

<ContentView xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamarinFormsNetwork.Views.IconButton">
    <ContentView.Content>
        <ContentPresenter/>
    </ContentView.Content>
</ContentView>

Although I think that this may be better

    public interface IFontIcon { 
        Color Color { get; set; }
        double Size { get; set; }
        string Glyph { get; set; }
        string FontFamily { get; set; }
    }

    public static class FontIconProperties
    {
        public static readonly BindableProperty GlyphProperty = BindableProperty.Create(nameof(IFontIcon.Glyph), typeof(string), typeof(IFontIcon));
        public static readonly BindableProperty ColorProperty = BindableProperty.Create(nameof(IFontIcon.Color), typeof(Color), typeof(IFontIcon));
        public static readonly BindableProperty SizeProperty = BindableProperty.Create(nameof(IFontIcon.Size), typeof(double), typeof(IFontIcon), 30d);
        public static readonly BindableProperty FontFamilyProperty = BindableProperty.Create(nameof(IFontIcon.FontFamily), typeof(string), typeof(IFontIcon));

        private static readonly List<BindableProperty> bindableProperties = new List<BindableProperty>
        {
            GlyphProperty,
            ColorProperty,
            SizeProperty,
            FontFamilyProperty
        };

        public static bool IsFontIconProperty(string propertyName)
        {
            return bindableProperties.Any(p => p.PropertyName == propertyName);
        }
    }

    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class FontIcon : ContentView, IFontIcon
    {
        public static readonly BindableProperty GlyphProperty = FontIconProperties.GlyphProperty;
        public static readonly BindableProperty ColorProperty = FontIconProperties.ColorProperty;
        public static readonly BindableProperty SizeProperty = FontIconProperties.SizeProperty;
        public static readonly BindableProperty FontFamilyProperty = FontIconProperties.FontFamilyProperty;

        private bool addedEffect;
        private bool initialized;

        public bool ApplyEffect { get; set; } = true;
        public FontIcon()
        {
            InitializeComponent();
        }

        protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            base.OnPropertyChanged(propertyName);
            if (initialized && FontIconProperties.IsFontIconProperty(propertyName))
            {
                Apply();
            }
        }

        protected override void OnChildAdded(Element child)
        {
            base.OnChildAdded(child);
            if (!(child is ContentPresenter))
            {
                Apply();
                initialized = true;
            }
        }

        private void Apply()
        {
            if(Glyph !=null && FontFamily != null)
            {
              switch (Content)
              {
                  case Label label:
                      SetUpLabel(label);
                      break;
                  case Button button:
                      SetupButton(button);
                      break;
                  case ImageButton imageButton:
                      SetupImageButton(imageButton);
                      break;
                  default:
                      throw new Exception(); // todo
              }
           }
        }

        private void SetUpLabel(Label label)
        {
            label.FontSize = Size;
            label.TextColor = Color;
            label.Text = Glyph;
            label.FontFamily = FontFamily;
        }

        private void SetupImageButton(ImageButton imageButton)
        {
            imageButton.Source = GetFontImageSource();
            AddEffect();
        }

        private void SetupButton(Button button)
        {
            button.ImageSource = GetFontImageSource();
            AddEffect();
        }

        private void AddEffect()
        {
            if (ApplyEffect && !addedEffect)
            {
                addedEffect = true;
                Content.Effects.Add(new FixButtonIconEffect());
            }
        }

        private FontImageSource GetFontImageSource()
        {
            return new FontImageSource
            {
                Size = Size,
                Color = Color,
                FontFamily = FontFamily,
                Glyph = Glyph
            };
        }

        public string Glyph
        {
            get => (string)GetValue(FontIcon.GlyphProperty);
            set => SetValue(FontIcon.GlyphProperty, value);
        }

        public Color Color
        {
            get => (Color)GetValue(FontIcon.ColorProperty);
            set => SetValue(FontIcon.ColorProperty, value);
        }

        public double Size
        {
            get => (double)GetValue(FontIcon.SizeProperty);
            set => SetValue(FontIcon.SizeProperty, value);
        }

        public string FontFamily
        {
            get => (string)GetValue(FontIcon.FontFamilyProperty);
            set => SetValue(FontIcon.FontFamilyProperty, value);
        }
    }


<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Namespace.FontIcon">
  <ContentView.Content>
        <ContentPresenter/>
    </ContentView.Content>
</ContentView>

Usage

            <views:FontIcon Color="{AppThemeBinding Light=Black,Dark=White}" Size="24" FontFamily="GoogleFontIcons" Glyph="{x:Static fonthelpers:GoogleFontIcons.AddCircleOutline}">
                <Button HorizontalOptions="End" IsEnabled="{Binding SomeCondition}" Command="{Binding SomeCommand}"/>
            </views:FontIcon>
            <views:FontIcon Color="Blue" Size="{AppThemeBinding Light=48,Dark=96}" FontFamily="GoogleFontIcons" Glyph="{x:Static fonthelpers:GoogleFontIcons.BackHand}">
                <ImageButton HorizontalOptions="Start"/>
            </views:FontIcon>
            <views:FontIcon Color="{AppThemeBinding Light=Green,Dark=Orange}" Size="126" FontFamily="GoogleFontIcons" Glyph="{x:Static fonthelpers:GoogleFontIcons.Alarm}">
                <Label HorizontalOptions="Center"/>
            </views:FontIcon>
    //https://fonts.google.com/icons
    internal static class GoogleFontIcons
    {
        public const string Alias = "GoogleFontIcons";
        public static string BackHand => Get("e764");
        public static string AddCircleOutline => Get("e148");
        public static string Alarm => Get("e855");
        //...
        private static string Get(string googles)
        {
            int p = int.Parse(googles, System.Globalization.NumberStyles.HexNumber);
            return Char.ConvertFromUtf32(p);
        }
    }

with [assembly: ExportFont("MaterialIconsOutlined-Regular.otf", Alias = GoogleFontIcons.Alias)] and with the font as an embedded resource.

@SujaVenkatesan
Copy link

The issue still occurs for Image also. How can we resolve this ?

@tonyhallett
Copy link
Contributor

tonyhallett commented Jan 21, 2022

@SujaVenkatesan
My pull request resolves minimize but with screenlock the icon will disappear and reappear shortly after so not the best resolution.
I believe I have tried all methods of drawing with the CanvasImageSource that Xamarin Forms current uses and they all suffer from the issue. In keeping with using an ImageSource for Button, ImageButton and Image I have tried to create an SvgImageSource and use that instead but unfortunately that is not currently rendering sharp like the broken CanvasImageSource or my workaround suggestion of a TextBlock or FontIcon. Perhaps you could look at my code for the SvgImageSource #15070 (comment) and see where I am going wrong.
Alternatively I have provided an effect that can be used to swap the Image renderered for a Button/ImageButton in UWP with a TextBlock. For an image a Xamarin forms Label works for me.

I am going to provide an alternative pull request that uses the FontIcon.

#15083

@pabloat81
Copy link

Hello, i still happening with ImageButton and a new Xamarin project created 2 months ago.

@hellyeahniels
Copy link

Also still happening with Button. Can this please be fixed???

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.