HomeAbout Me

Xamarin Forms Renderer Property

By Michael Stonis
Published in Development
January 28, 2019
Xamarin Forms Renderer Property

Finding Hidden Treasures

I often times find myself digging through Xamarin.Forms source code looking for how they implemented features or to see why something is behaving in a certain way. Along that path, I have come across a whole bunch of fun things. If you are serious about learning mobile development or Xamarin.Forms, I cannot recommend enough taking a look through the source code.

In one of my journeys, I came across an internal property that seems to be used quite often throughout Xamarin.Forms. It was the Renderer property and I could find examples of it in iOS, Android, and a bunch o’ other places.

internal static readonly BindableProperty RendererProperty =
BindableProperty.CreateAttached("Renderer", typeof(IVisualElementRenderer), typeof(Platform), default(IVisualElementRenderer),
propertyChanged: (bindable, oldvalue, newvalue) =>
{
var view = bindable as VisualElement;
if (view != null)
view.IsPlatformEnabled = newvalue != null;
});

Importance of the Renderer

As a Xamarin.Forms developer, when we want to create a new component for Xamarin.Forms that provides a bridge to the platform/native world, we typically do this by creating Custom Renderers. This process is the same core concept that the team uses to create all of the built-in controls for Xamarin.Forms.

I figured this property had to be related, and it is. What the Renderer property does is notify our Xamarin.Forms components that the native component has been built and attached to our Xamarin.Forms implementation of that component. You can think if it this way, when we create a user interface component, whether it is a page or a button or whatever, it is really just a definition of that control and how we would like it to be configured. It isn’t until a renderer is attached that those control configurations are interpreted and applied to the native component.

This is really powerful because it gives us a really easy way to hook into and listen when our components are really in use. There are many cases where we can now leverage this such as subscribing and unsubscribing to events, starting and stopping timers, or providing earlier calls to load data. One neat thing about this property is that it actually is like an additional layer to the lifecycle of Xamarin.Forms controls which will get called before OnAppearing and after OnDisappearing. The difference in timing between when the Renderer is attached and when the actual page’s OnAppearing method is called can be pretty substantial. For example, on iOS, the average difference is 515ms. This is definitely significant enough for a user to notice data missing, for example, as a page is being navigated in.

Listening for Renderer Changes

In order to listen to this property, you can add some code to your pages or custom views like the following:

protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
base.OnPropertyChanged(propertyName);
if (propertyName.Equals("Renderer", StringComparison.OrdinalIgnoreCase))
{
}
}

Unfortunately, this is only a starting point, as this just let’s us know that there was a change to the renderer. It does not let us know if the renderer itself was attached or detached. To detect that we will need some additional code.

First, we can create an interface that we will use to inspect the Renderer state of a VisualElement.

public interface IRendererResolver
{
object GetRenderer(VisualElement element);
bool HasRenderer(VisualElement element);
}

Next we need to provide the implementations of that interface for each platform. Below, I have examples of how this can be done on Android and iOS. This same technique should work on all platforms though.

[assembly: Dependency(typeof(AndroidRendererResolver))]
namespace RendererExample.Droid
{
public class AndroidRendererResolver : IRendererResolver
{
public object GetRenderer(VisualElement element)
{
return Xamarin.Forms.Platform.Android.Platform.GetRenderer(element);
}
public bool HasRenderer(VisualElement element)
{
return GetRenderer(element) != null;
}
}
}
[assembly: Dependency(typeof(IosRendererResolver))]
namespace RendererExample.iOS
{
public class IosRendererResolver : IRendererResolver
{
public object GetRenderer(VisualElement element)
{
return Xamarin.Forms.Platform.iOS.Platform.GetRenderer(element);
}
public bool HasRenderer(VisualElement element)
{
return GetRenderer(element) != null;
}
}
}

Finally, we can update our OnPropertyChanged method and use this to inspect when our component does have a renderer.

protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
base.OnPropertyChanged(propertyName);
if(propertyName.Equals("Renderer", StringComparison.OrdinalIgnoreCase))
{
var rr = DependencyService.Get<IRendererResolver>();
if(rr.HasRenderer(this))
{
//do setup work
}
else
{
// do shutdown work
}
}
}

Code Sample

I have a sample of the code above with a really simple app that can be found on my github.

Final Notes

A few final notes on using this technique. Please keep in mind that this property is not directly exposed to use as developers and as such, the internals of how this changes could easily change one day and this could all stop working. I have had chats with the Xamarin.Forms team in the past and they support this technique, for now. Additionally, there is a proposal in Xamarin.Forms to get better lifecycle events, but as of this writing it hasn’t really made any headway, so this will likely be the best alternative for a bit.


Tags

#development

Share

Previous Article
Quick Tip - Accessing The Service Provider in .NET MAUI

Quick Links

Eight-BotAbout Me

Social Media