XamarinForms – Overriding a ContentPage with a custom PageRenderer on Android

The short version:

If you want to have default behavior on all platforms except for Android. Do the following

  • Create your content page with all of it’s goodies
  • Create a custom PageRenderer
  • In the OnElementChanged function
    • Cast the Context property of the renderer to the Activity type associated
    • Take that value and execute the SetContentView method while passing in your new view!

The awesome story version:

I’m building a Xamarin Forms application which has a default implementation in most platforms and a special snowflake behavior on Android. While working through the Xamarin documentation I realized that custom renderers were the way to go.

The default behavior for all platforms is performed through a WebView, which has some custom logic when pages are navigated. While the functionality still works, it doesn’t feel seamless. However, in Android there is already native functionality that really improves the user experience, so I wanted to make sure that if I was on the Android platform, I would not display that web view and I would go straight to the native view.

While navigating through the sea of googlementation(http://bfy.tw/7ljK), I saw various examples on how to do stuff with custom renderers and I walked through them one by one hoping to hit my use case. Most search results demonstrated how to replace the view when doing a custom renderer on a control element with the SetNativeControl method. However, this was a page renderer. So it was time for experiments!

The behavior that I got was that the default view was still rendering and my custom view was not. If commented out the content on the default ContentPage then it would successfully output the custom view. However that in turn meant no default behavior. There was no winning! It was like arguing with a cat!

Hours later, I was able to debug and discover that I had access to the Android Activity. Using the activity, I was able to replace the main content with the SetContentView method like so.

...
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;

[assembly: ExportRenderer(typeof(FooPage), typeof(FooPageRenderer))]
namespace FooApp.Droid.Renderer
{
    public class FooPageRenderer : PageRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs e)
        {
            base.OnElementChanged(e);
            var activity = this.Context as MainActivity;
            var newView = activity.FindViewById(Resource.Layout.foo);
            activity.SetContentView(newView);
        }

    }
}

I hope this helps other people save a few hours in their quests of building awesome things.

Keep on coding on friends!