Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Mobile / Xamarin

Xamarin Frame and Disappearing Outline

5.00/5 (3 votes)
23 Apr 2016CPOL3 min read 14.8K  
This is another article in the mini series of Xamarin bugs and workarounds for them. 

This is another article in the mini series of Xamarin bugs and workarounds for them Smile. You can find the previous one here.

This one is again about Frame control. I promise that another one will be about something else Cool.

Issue can be observed when you bind BackgroundColor property of Frame control and something will trigger change of view model source property binded to background color.

But since one image is more than a thousand words - here is a gif with this bug.

Frame is quite big (it is good to open image in new window), but if you look near corners of it, you will see that they are round when the background is green and when the background changes to Red, they disappear. Outline disappears too. It is a slight white line around green field (it is even less visible), but I assure you that it is there. Smile

Ok, here is a code for this example in XAML App file:

XML
<?xml version="1.0" encoding="utf-8" ?>
<Application xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="FrameBug.App">
  <Application.MainPage>
    <ContentPage>
      <Grid>
        <Grid.RowDefinitions>
          <RowDefinition Height="*"></RowDefinition>
          <RowDefinition Height="*"></RowDefinition>
        </Grid.RowDefinitions>
        <StackLayout Padding="10">
          <Frame VerticalOptions="Center" OutlineColor="White" 
                 BackgroundColor="{Binding LineColor}">
            <Label Text="Label" FontSize="60" />
          </Frame>
        </StackLayout>
        <Button Text="Change color" Grid.Row="1" 
        Command="{Binding ChangeColor}"></Button>
      </Grid>
    </ContentPage>
  </Application.MainPage>
</Application>

One noticeable thing done in code behind for this XAML file is adding BindingContext instance (App.xaml.cs file):

C#
namespace FrameBug
{
    public partial class App
    {
        public App()
        {
            InitializeComponent();
            BindingContext = new AppViewModel();
        }
    }
}

Ok. One missing piece is AppViewModel class:

C#
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Input;
using Xamarin.Forms;

namespace FrameBug
{
    public class AppViewModel : INotifyPropertyChanged
    {
        private ICommand _changeColor;

        public event PropertyChangedEventHandler PropertyChanged;

        public ICommand ChangeColor => _changeColor ?? (_changeColor = new Command(OnChangeColor));

        public Color LineColor { get; set; } = Color.Green;

        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        private void OnChangeColor()
        {
            LineColor = Color.Red;
            OnPropertyChanged("LineColor");
        }
    }
}

As you may notice, it is C# code from VS2015. It will not work in prior versions, but there is no real point in doing tests for Xamarin apps in earlier versions since Xamarin recently was released for free even in VS2015 community edition.

With this simple application added to Xamarin template solution, we can reproduce bugs as in the above gif. But how to fix it? And why does it occur? It took some digging and Resharper help with decompiling FrameRenderer to find out why. Yes, this class that renders Frame control as plain bitmap in Android environment is the source of the problem. This class (since there is no public source code, I will not include disassembled source code) has private UpdateBackground method which creates custom class based on Android Drawable class. In its bowels, it renders Background with solid color from BackgroundColor property of Frame control and Outline as a line (or rather rounded rectangle) from OutlineColor property of Frame. Those methods are supposed to work either on initialization or BackgroundColor, OutlineColor properties change according to code of mentioned class. All good, right? When I created a very similar class, attached it to custom renderer of Frame, both operations were performed correctly during the debugging session. But still, the frame was drawn incorrectly. It took some experimentation with height and width of rectangles in bitmap and it struck me that nothing happens even with 1x1 px size, because background is rerendered in some other place. Where? Since no more code in renderer was touching drawing, it had to be in base class - VisualElementRenderer<>. If you disassemble it too, you will see that it has SetBackgroundColor and it was my first suspect. This method is executed on property change of control. Which one you ask? Smile BackgroundColor property. I did not dig any further since it is a virtual method. Quick test proved my suspect guilty. Adding the following override method to custom renderer fixed this problem.

C#
public override void SetBackgroundColor(Color color)
{

}

Since code in FrameRenderer and its base class was executing correctly, it probably was executing in the wrong order. Probably it was missed by Xamarin Team after fixing this bug and closing this one. Or maybe this method as virtual was added after fix and tests and testers missed it? Nevertheless, writing new custom renderer based on Xamarin one fix this issue:

C#
[assembly: ExportRenderer(typeof(Frame), typeof(FrameRenderer))]
namespace FrameBug.Droid
{
    public class FrameRenderer : Xamarin.Forms.Platform.Android.FrameRenderer
    {
        public override void SetBackgroundColor(Color color)
        {
            //base.SetBackgroundColor(color);
        }
    }
}

Commented line can be used to trigger the buggy behavior if anyone is curious enough Smile.

This is another one of my unpleasant adventures with Xamarin framework. I hope this can be helpful and spare you some time. You can find a sample application at the top of this post.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)