简体   繁体   中英

Override nav bar back button click on Xamarin.Forms

I have form in xamarin.forms and I want to show a popup message when users click on the nav bar button if there are pending data to save. I found this example but it doesn't not working on Xamarin.Forms 5.0

Any idea of how to do it?

I did a quick test on this you can refer to it.

First, I create a contentpage and set CustomBackButtonAction , EnableBackButtonOverride to add navigate method:

public partial class TestPage5 : ContentPage
    {public Action CustomBackButtonAction { get; set; }
        public static readonly BindableProperty EnableBackButtonOverrideProperty = BindableProperty.Create(
            nameof(EnableBackButtonOverride),
            typeof(bool),
            typeof(TestPage5),
            false
            );
          public bool EnableBackButtonOverride {

            get { return (bool)GetValue(EnableBackButtonOverrideProperty); }
            set { SetValue(EnableBackButtonOverrideProperty, value); }
        }
        public TestPage5()
        {
            InitializeComponent();
            EnableBackButtonOverride = true;
            CustomBackButtonAction = async () => { var result = await DisplayAlert("Alert", "Are you Sure?", "Yes", "No");
                if (result)
                { await Navigation.PopAsync(true); }     };
        }  
        }

Then create renderer on ios while override OnOptionsItemSelected on android:

ios(create a new backbutton and override):

[assembly:ExportRenderer(typeof(TestPage5),typeof(MyRenderer))]
namespace My_Forms_Test3.iOS
{
    public class MyRenderer:Xamarin.Forms.Platform.iOS.PageRenderer
    {
        public override void ViewWillAppear(bool animated)
        {
            base.ViewWillAppear(animated);
            if (((TestPage5)Element).EnableBackButtonOverride)
            {
                SetButton();
            }
        }

           private void SetButton()
            {
            var backbuttonimg = UIImage.FromBundle("backarrow.png");
            backbuttonimg = backbuttonimg.ImageWithRenderingMode(UIImageRenderingMode.AlwaysTemplate);
            var backbutton = new UIButton(UIButtonType.Custom)
            { HorizontalAlignment=UIControlContentHorizontalAlignment.Left,
            TitleEdgeInsets=new UIEdgeInsets(11.5f,15f,10f,0f),
            ImageEdgeInsets=new UIEdgeInsets(1f,8f,0f,0f)};
            backbutton.SetTitle("Back", UIControlState.Normal);
            backbutton.SetTitleColor(UIColor.White, UIControlState.Normal);
            backbutton.SetTitleColor(UIColor.LightGray, UIControlState.Highlighted);
            backbutton.Font = UIFont.FromName("HelveticaNeue", (nfloat)17);
            backbutton.SetImage(backbuttonimg, UIControlState.Normal);
            backbutton.SizeToFit();
            backbutton.TouchDown += (sender, e) =>
            {
                if (((TestPage5)Element)?.CustomBackButtonAction != null)
                {
                    ((TestPage5)Element)?.CustomBackButtonAction.Invoke();

                }
            };
            backbutton.Frame = new CoreGraphics.CGRect(0, 0, UIScreen.MainScreen.Bounds.Width / 4,
                NavigationController.NavigationBar.Frame.Height);
            var buttoncontainer = new UIView(new CoreGraphics.CGRect(0, 0, backbutton.Frame.Width, backbutton.Frame.Height));
            buttoncontainer.AddSubview(backbutton);
            var fixspace = new UIBarButtonItem(UIBarButtonSystemItem.FixedSpace)
            { Width = -16f };
            var backbuttonitem = new UIBarButtonItem("", UIBarButtonItemStyle.Plain, null) { CustomView = backbutton };
            NavigationController.TopViewController.NavigationItem.LeftBarButtonItems = new[] { fixspace, backbuttonitem };
        }
    }
    }

android:

add following on main activity:

protected override void OnCreate(Bundle savedInstanceState)
        {
            TabLayoutResource = Resource.Layout.Tabbar;
            ToolbarResource = Resource.Layout.Toolbar;
 
            base.OnCreate(savedInstanceState);
 
            Xamarin.Essentials.Platform.Init(this, savedInstanceState);
            global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
            LoadApplication(new App());
 
            //important to trigger OnOptionItemSelected
            Android.Support.V7.Widget.Toolbar toolbar
             = this.FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.toolbar);
            SetSupportActionBar(toolbar);

also in mainactivity.cs:

public override bool OnOptionsItemSelected(IMenuItem item)
        {
            // check if the current item id 
            // is equals to the back button id
            if (item.ItemId == 16908332) // xam forms nav bar back button id
            {
                // retrieve the current xamarin 
                // forms page instance
                var currentpage = (TestPage5)Xamarin.Forms.Application.Current.
                     MainPage.Navigation.NavigationStack.LastOrDefault();
 
                // check if the page has subscribed to the custom back button event
                if (currentpage?.CustomBackButtonAction != null)
                {
                    // invoke the Custom back button action
                    currentpage?.CustomBackButtonAction.Invoke();
                    // and disable the default back button action
                    return false;
                }
 
                // if its not subscribed then go ahead 
                // with the default back button action
                return base.OnOptionsItemSelected(item);
            }
            else
            {
                // since its not the back button 
                //click, pass the event to the base
                return base.OnOptionsItemSelected(item);
            }
        }
 
        //android Hardware back button event
        public override void OnBackPressed()
        {
            // this is really not necessary, but in Android user has both Nav bar back button 
            // and physical back button, so its safe to cover the both events
 
            var currentpage = (BaseContentPage)Xamarin.Forms.Application.Current.
                MainPage.Navigation.NavigationStack.LastOrDefault();
 
            if (currentpage?.CustomBackButtonAction != null)
            {
                currentpage?.CustomBackButtonAction.Invoke();
            }
            else
            {
                base.OnBackPressed();
            }
        }

Here is the full blog I have written which handles the same,

Android:

I have used NavigationPage Renderer to achieve this functionality in android

Android Implementtion

iOS:

I have used Page Renderer to achieve this functionality in iOS

public class CustomPageRenderer:PageRenderer
{
    public override void ViewWillAppear(bool animated)
    {
        base.ViewWillAppear(animated);
        if (Element != null && Element is BasePage basePage && basePage.BindingContext != null &&
            basePage.BindingContext is BaseViewModel baseViewModel)
        {
            SetCustomBackButton(baseViewModel);
        }
    }
    private void SetCustomBackButton(BaseViewModel baseViewModel)
    {
        UIButton btn = new UIButton();
        btn.Frame = new CGRect(0, 0, 50, 40);
        btn.BackgroundColor = UIColor.Clear;

        btn.TouchDown += (sender, e) =>
        {
            // Whatever your custom back button click handling
            baseViewModel.BackPressedAction?.Invoke(false);
        };
        //var views = NavigationController?.NavigationBar.Subviews;
        NavigationController?.NavigationBar.AddSubview(btn);
    }
}

Note:

Do create BackPressedAction Action in your base view model to capture the back press event

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM