Xamarin.Forms – CustomMasterDetailPage – change navigation icon(back button and “hamburger” menu button) – Android

This project, you can find at: https://github.com/officialdoniald/Xamarin.Forms.CustomControls

We have to create some platforms specific code (custom NavigationPage Renderer), because in the base Xamarin.Forms code, we can’t find this implementation. So first step, we have to create a class in the .NET Standard/PCL project: CustomMasterDetailPage( https://github.com/officialdoniald/Xamarin.Forms.CustomControls/blob/master/XamarinForms.CustomControls/XamarinForms.CustomControls/MasterDetailPage/CustomMasterDetailPage.cs ).

using Xamarin.Forms; 
using XamarinForms.CustomControls.Enums;
namespace XamarinForms.CustomControls.MasterDetailPage
{
public class CustomMasterDetailPage : Xamarin.Forms.MasterDetailPage
{
public CustomMasterDetailPage() : base() { }
}
}

In Xamarin.Android, we can say that: ok, let’s change the back button and the hamburger menu button image forever, so we have to write an algorythm, that will change our icons. We have to watch the NavigationStack, and it’s count is equal to 1, we have to change the icon to a hamburger menu icon, otherwise we have to change the icon to a back button icon. Let’s do it.

So we have to write a MasterDetailPageRenderer class in the Android Project and we have to write the algorythm there.

 using Android.Content;
 using Android.Graphics;
 using Android.Graphics.Drawables;
 using Android.Runtime;
 using Android.Widget;
 using Plugin.CurrentActivity;
 using System;
 using Xamarin.Forms;
 using Xamarin.Forms.Platform.Android.AppCompat;
 using XamarinForms.CustomControls.Droid.CustomRenderer;
 using XamarinForms.CustomControls.MasterDetailPage;

[assembly: ExportRenderer(typeof(CustomMasterDetailPage), typeof(MasterNavigationPageRenderer))]
 namespace XamarinForms.CustomControls.Droid.CustomRenderer
 { 
pragma warning disable CS0618
public class MasterNavigationPageRenderer : MasterDetailPageRenderer
{
    private static Android.Support.V7.Widget.Toolbar GetToolbar() => (CrossCurrentActivity.Current?.Activity as MainActivity)?.FindViewById(Resource.Id.toolbar);

    private Android.Support.V7.Widget.Toolbar toolbar;

    public MasterNavigationPageRenderer() : base() { }

    public MasterNavigationPageRenderer(Context context) : base(context) { }

    public MasterNavigationPageRenderer(IntPtr a, JniHandleOwnership b) : base() { }

    protected override void OnLayout(bool changed, int l, int t, int r, int b)
    {
        base.OnLayout(changed, l, t, r, b);
        toolbar = GetToolbar();

        if (toolbar != null)
        {
            if (GlobalVariables.NavigationStackCount == 1)
            {
                SetNavigationButton(Resource.Drawable.menu);
            }
            else
            {
                SetNavigationButton(Resource.Drawable.back);
            }
        }
    }

    private void SetNavigationButton(int resourceID)
    {
        var icon = Forms.Context.GetDrawable(resourceID);
        using (var drawable = ((BitmapDrawable)icon).Bitmap)
        using (var bitmap = Bitmap.CreateScaledBitmap(drawable, 80, 80, false))
        using (var newDrawable = new BitmapDrawable(Resources, bitmap))
        {
            toolbar.NavigationIcon = newDrawable;
        }
    }
}
pragma warning restore CS0618
}
GlobalVariables.NavigationStackCount is public static variable. You can defined it in the .NET Standard/PCL project.

Next step, we have to create a platform specific NavigationPageRenderer. So let’s create a class in the .NET Standard/PCL project, and call it CustomNavigationPage ( https://github.com/officialdoniald/Xamarin.Forms.CustomControls/blob/master/XamarinForms.CustomControls/XamarinForms.CustomControls/NavigationPage/CustomNavigationPage.cs )

Than we have to create the platform spec. implementation in the Android project (https://github.com/officialdoniald/Xamarin.Forms.CustomControls/blob/master/XamarinForms.CustomControls/XamarinForms.CustomControls.Android/CustomRenderer/CustonNavigationPageRenderer.cs) :

 using System;
 using System.ComponentModel;
 using System.Threading.Tasks;
 using Android.Content;
 using Android.Graphics;
 using Android.Graphics.Drawables;
 using Android.Runtime;
 using Android.Support.V7.Graphics.Drawable;
 using Android.Util;
 using Android.Widget;
 using Plugin.CurrentActivity;
 using Xamarin.Forms;
 using Xamarin.Forms.Platform.Android;
 using XamarinForms.CustomControls.Droid.CustomRenderer;
 using XamarinForms.CustomControls.Enums;
 using XamarinForms.CustomControls.NavigationPage;
[assembly: ExportRenderer(typeof(CustomNavigationPage), typeof(CustonNavigationPageRenderer))] 
namespace XamarinForms.CustomControls.Droid.CustomRenderer
 { 
pragma warning disable CS0618
public class CustonNavigationPageRenderer : Xamarin.Forms.Platform.Android.AppCompat.NavigationPageRenderer
{
    #region Properties

    private static Android.Support.V7.Widget.Toolbar GetToolbar() => (CrossCurrentActivity.Current?.Activity as MainActivity)?.FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.toolbar);

    private Android.Support.V7.Widget.Toolbar toolbar;

    private Page view;

    #endregion

    #region Kontruktor

    public CustonNavigationPageRenderer() : base() { }

    public CustonNavigationPageRenderer(Context context) : base(context) { }

    public CustonNavigationPageRenderer(IntPtr a, JniHandleOwnership b) : base() { }

    #endregion

 protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        base.OnElementPropertyChanged(sender, e);

        var page = (CustomNavigationPage)sender;
    }

    protected override void OnLayout(bool changed, int l, int t, int r, int b)
    {
        base.OnLayout(changed, l, t, r, b);

        //You can define special pages, where you can change the rule.
        //if (view is LoginPage || view is PasswordResetPage || view is RegisterPage || view is SMSPage)
        //{
        //    SetIcon();
        //}
    }

    protected override Task<bool> OnPushAsync(Page view, bool animated)
    {
        var retVal = base.OnPushAsync(view, animated);

        GlobalVariables.NavigationStackCount = view.Navigation.NavigationStack.Count;

        this.view = view;

        return retVal;
    }

    protected override Task<bool> OnPopToRootAsync(Page page, bool animated)
    {
        var retVal = base.OnPopToRootAsync(page, animated);

        GlobalVariables.NavigationStackCount = page.Navigation.NavigationStack.Count - 1;

        view = page;

        return retVal;
    }

    protected override Task<bool> OnPopViewAsync(Page page, bool animated)
    {
        var retVal = base.OnPopViewAsync(page, animated);

        GlobalVariables.NavigationStackCount = page.Navigation.NavigationStack.Count - 1;

        view = page;

        return retVal;
    }

    private void SetIcon()
    {
        toolbar = GetToolbar();

        if (toolbar != null)
        {
            if (GlobalVariables.NavigationStackCount == 1)
            {
                SetNavigationButton(Resource.Drawable.menu);
            }
            else
            {
                SetNavigationButton(Resource.Drawable.back);
            }
        }
    }

    private void SetNavigationButton(int resourceID)
    {
        var icon = Forms.Context.GetDrawable(resourceID);
        using (var drawable = ((BitmapDrawable)icon).Bitmap)
        using (var bitmap = Bitmap.CreateScaledBitmap(drawable, 80, 80, false))
        using (var newDrawable = new BitmapDrawable(Resources, bitmap))
        {
            toolbar.NavigationIcon = newDrawable;
        }
    }
}
pragma warning restore CS0618
}

Here the most important thing is to store the GlobalVariables.NavigationStackCount. Here can change the icons too.