Custom Picker Renderer on Android

December 4th 2020 Xamarin

The default Picker view for Android in Xamarin.Forms includes an underline:

Default Xamarin.Forms Picker with underline

To get rid of it, a custom renderer is required:

[assembly: ExportRenderer(typeof(BorderlessPicker), typeof(BorderlessPickerRenderer))]
namespace AndroidPickerRenderer.Droid.Renderers
{
    public class BorderlessPickerRenderer : PickerRenderer
    {
        public BorderlessPickerRenderer(Context context) : base(context)
        {
        }

        protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
        {
            base.OnElementChanged(e);

            if (e.OldElement == null)
            {
                Control.Background = null;
            }
        }
    }
}

The target BorderlessPicker control can trivially derive from the original Picker control:

public class BorderlessPicker : Picker
{
}

The new control is rendered without the underline as desired (you can always add a Xamarin.Forms ImageButton as a dropdown icon on the right if you want to):

Custom Xamarin.Forms Picker without underline

However, the new control will have another undesired side effect in rendering. This was the popup of the original control:

Popup of the default Xamarin.Forms Picker

The new control with the custom renderer opens a different popup:

Popup of the custom Xamarin.Forms Picker

How is this possible? Why would the rendering change if the new renderer derives from the original one, and only prevents the underline from rendering?

This can happen because there are two renderers for Picker on Android. One in the Xamarin.Forms.Platform.Android namespace where most renderers reside. And a different one in the Xamarin.Forms.Platform.Android.AppCompat namespace.

As it turns out, the latter is the default one. And the custom renderer above happens to derive from the former. This is likely to happen not only because you expect the renderer to be in the same namespace as most others, but also because the ElementChangedEventArgs class used in the overridden method is in that same namespace.

To get the same popup as by default, the base renderer must be derived from the AppCompat sub-namespace:

[assembly: ExportRenderer(typeof(BorderlessPicker), typeof(BorderlessPickerRenderer))]
namespace AndroidPickerRenderer.Droid.Renderers
{
    public class BorderlessPickerRenderer
        : Xamarin.Forms.Platform.Android.AppCompat.PickerRenderer
    {
        public BorderlessPickerRenderer(Context context) : base(context)
        {
        }

        protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
        {
            base.OnElementChanged(e);

            if (e.OldElement == null)
            {
                Control.Background = null;
            }
        }
    }
}

You can find a working sample in my GitHub repository. Both custom renderers are implemented as separate commits.

Creating a custom renderer for a Xamarin.Forms control might result in other changes in addition to the ones intentionally implemented. If that happens to you, make sure you are deriving from the correct base renderer.

Get notified when a new blog post is published (usually every Friday):

If you're looking for online one-on-one mentorship on a related topic, you can find me on Codementor.
If you need a team of experienced software engineers to help you with a project, contact us at Razum.
Copyright
Creative Commons License