Understanding how your UI code ends up by being shapes and colors on a device screen may help in better usage, better interpretation for encountered issues and for evaluating if you really need a custom renderer for a given control.
Exploring this also explains some of the interesting internal Xamarin forms mechanisms to play its awesome cross-platform game!
To do this, I will here attempt to explore the journey of a simple control: The Label.
For simplicity, I will track the journey to an Android device.
Label is a class defined Xamarin.Forms.Core.dll. Its hierarchy is roughly the following:
The Label renderer
At the end of this simple control processing chain, we have the android's Label renderer responsible of drawing it on the screen of each device… The renderer hierarchy (on Android):
The mono component
The ViewRenderer (XF Platform.Android.dll) is defined as:
public abstract class ViewRenderer<TView, TNativeView>
It inherits the VisualElementRenderer (abstract) class which is defined as:
public abstract class VisualElementRenderer<TElement>
where TElement : VisualElement
Both of them refer to objects from the Android.Views and Android.Runtime namespaces defined in Mono.Android.dll.
The Label renderer referenced types, inheritance and interface implementations (summary)
Label renderer calls (summary)
The IRegisterable and Registrar link nodes
As you can notice, both ViewRenderer and VisualElementRenderer implement the IRegisterable Interface defined in XF.Core.dll.
IRegistrable is implemented by all Renderers. And is referenced by the Registrar class (we will see later).
Let us follow IRegistrable implemented child tree till the Label Renderer node:
(Incidentally, in he above figure, we again notice our ImageSourceHandler at the very root of the tree… you may look at my notes here)
The key node: XF Registrar
Registrar (Xamarin.Forms.Core.dll) is an internal static class. It exposes (internally) some methods for registering generic handlers of which we find the Renderers.
The method RegisterAll(Type attrTypes) of the Registrar class proceeds registration of the input array of types (note: as the class is internal, I could not find – for now – who calls this method: see a sample call at the end of this post).
If we follow the method's decompiled code, we understand that items of the Type array argument are expected to be each decorated with an attribute of type HandlerAttribute (deriving from System.Attribute). Here is a simplified version of the method's code:
internal static void RegisterAll(Type attrTypes)
Assembly assemblies = Device.GetAssemblies();
foreach (Assembly assembly2 in assemblies)
foreach (Type type in attrTypes)
Attribute attributeArray = Enumerable.ToArray<Attribute>(CustomAttributeExtensions.GetCustomAttributes(assembly2, type));
foreach (HandlerAttribute attribute in attributeArray)
HandlerAttribute exposes two members: HandlerType and TargetType, both are of type System.Type
Few attributes derive from this HandlerType attribute. Of which you have… oh… the famous ExportRendererAttribute!
And that is roughly how XF finds the renderer to call for a given control on each platform.
Sample call to Registrar at droid application launch in AppCompat: