How to handle / cancel navigation in Xamarin formats

I tried using reverse navigation by overriding OnBackButtonPressed , but somehow it wasn't called at all. I am using ContentPage and the latest version 1.4.2.

+14
source share
9 answers

Well, after many hours, I realized this. There are three parts.

# 1 Handling the hardware button on Android. It is easy to override OnBackButtonPressed. Remember that this is only for the back button and Android. It will not process the navigation bar button. As you can see, I tried to return through the browser before retreating from the page, but you can put any logic that you need.

  protected override bool OnBackButtonPressed() { if (_browser.CanGoBack) { _browser.GoBack(); return true; } else { //await Navigation.PopAsync(true); base.OnBackButtonPressed(); return true; } } 

# 2 iOS navigation button. It was very difficult if you look around the Internet, you will find a couple of examples of replacing the back button with a new custom button, but it is almost impossible to make it look like your other pages. In this case, I made a transparent button that sits on top of a regular button.

 [assembly: ExportRenderer(typeof(MyAdvantagePage), typeof (MyAdvantagePageRenderer))] namespace Advantage.MyAdvantage.MobileApp.iOS.Renderers { public class MyAdvantagePageRenderer : Xamarin.Forms.Platform.iOS.PageRenderer { public override void ViewWillAppear(bool animated) { base.ViewWillAppear(animated); if (((MyAdvantagePage)Element).EnableBackButtonOverride) { SetCustomBackButton(); } } private void SetCustomBackButton() { 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 if (((MyAdvantagePage)Element)?. CustomBackButtonAction != null) { ((MyAdvantagePage)Element)?. CustomBackButtonAction.Invoke(); } }; NavigationController.NavigationBar.AddSubview(btn); } } } 

Android is difficult. In earlier versions and future versions of Forms, after patching, you can simply override OnOptionsItemselected like this

  public override bool OnOptionsItemSelected(IMenuItem item) { // check if the current item id // is equals to the back button id if (item.ItemId == 16908332) { // retrieve the current xamarin forms page instance var currentpage = (MyAdvantagePage) 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); } } 

However, if you are using FormsAppCompatActivity, you need to add this to your OnCreate in MainActivity to install the toolbar:

 Android.Support.V7.Widget.Toolbar toolbar = this.FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.toolbar); SetSupportActionBar(toolbar); 

But wait! If your version of .Forms is too old or too new, an error occurs when the toolbar is null. If this happens, hacked together the way I got the job to make the deadline is as follows. In OnCreate in MainActivity :

  MobileApp.Pages.Articles.ArticleDetail.androdAction = () => { Android.Support.V7.Widget.Toolbar toolbar = this.FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.toolbar); SetSupportActionBar(toolbar); }; 

ArticleDetail is the page, androidAction is the Action that I run on OnAppearing if the Android platform is on my page. At this point in your application, the toolbar will no longer be zero.

A few more steps, the iOS rendering we made above uses properties that you need to add to any page you create the rendering on. I did this for my MyAdvantagePage class that I created that implements ContentPage. So in the class MyAdvantagePage I added

 public Action CustomBackButtonAction { get; set; } public static readonly BindableProperty EnableBackButtonOverrideProperty = BindableProperty.Create( nameof(EnableBackButtonOverride), typeof(bool), typeof(MyAdvantagePage), false); /// <summary> /// Gets or Sets Custom Back button overriding state /// </summary> public bool EnableBackButtonOverride { get { return (bool)GetValue(EnableBackButtonOverrideProperty); } set { SetValue(EnableBackButtonOverrideProperty, value); } } 

Now that it's all done, on any of my MyAdvantagePage I can add this

 : this.EnableBackButtonOverride = true; this.CustomBackButtonAction = async () => { if (_browser.CanGoBack) { _browser.GoBack(); } else { await Navigation.PopAsync(true); } }; 

That should be all to make it work on Android hardware, and switch back to both Android and iOS.

+16
source

You are right, in the page class, override OnBackButtonPressed and return true if you want to prevent navigation. It works great for me, and I have the same version.

 protected override bool OnBackButtonPressed() { if (Condition) return true; return base.OnBackButtonPressed(); } 
+12
source

Depending on what you are looking for (I would not recommend using this if you just want to cancel the back button navigation), OnDisappearing may be another option:

 protected override void OnDisappearing() { //back button logic here } 
+3
source

OnBackButtonPressed () will be called when the hardware return button is pressed, as in android. This will not work on the software return button, as in ios.

+1
source

For everyone who is still struggling with this problem - basically you can’t intercept the reverse navigation cross platform. Having said that, there are two approaches that effectively solve the problem:

For UWP you yourself :)

Edit:

Well, it’s not the way I did it :) It actually turned out to be quite simple - there is only one back button and is supported by forms, so you just need to override ContentPages OnBackButtonPressed:

  protected override bool OnBackButtonPressed() { if (Device.RuntimePlatform.Equals(Device.UWP)) { OnClosePageRequested(); return true; } else { base.OnBackButtonPressed(); return false; } } async void OnClosePageRequested() { var tdvm = (TaskDetailsViewModel)BindingContext; if (tdvm.CanSaveTask()) { var result = await DisplayAlert("Wait", "You have unsaved changes! Are you sure you want to go back?", "Discard changes", "Cancel"); if (result) { tdvm.DiscardChanges(); await Navigation.PopAsync(true); } } else { await Navigation.PopAsync(true); } } 
+1
source

The trick is to implement your own navigation page, which inherits from NavigationPage . It has the corresponding events Pushed , Popped and PoppedToRoot .

An example implementation might look like this:

 public class PageLifetimeSupportingNavigationPage : NavigationPage { public PageLifetimeSupportingNavigationPage(Page content) : base(content) { Init(); } private void Init() { Pushed += (sender, e) => OpenPage(e.Page); Popped += (sender, e) => ClosePage(e.Page); PoppedToRoot += (sender, e) => { var args = e as PoppedToRootEventArgs; if (args == null) return; foreach (var page in args.PoppedPages.Reverse()) ClosePage(page); }; } private static void OpenPage(Page page) { if (page is IPageLifetime navpage) navpage.OnOpening(); } private static void ClosePage(Page page) { if (page is IPageLifetime navpage) navpage.OnClosed(); page.BindingContext = null; } } 

Pages implement the following interface:

 public interface IPageLifetime { void OnOpening(); void OnClosed(); } 

This interface can be implemented in the base class for all pages, and then delegate to it the presentation model calls.

The navigation page can be created as follows:

 var navigationPage = new PageLifetimeSupportingNavigationPage(new MainPage()); 

MainPage will be the root page to display.

Of course, you can also just use NavigationPage and subscribe to it without inheriting it.

+1
source
  protected override bool OnBackButtonPressed() { base.OnBackButtonPressed(); return true; } 

base.OnBackButtonPressed() returns false when the hardware return button is clicked. In order to prevent the button "Back" or go to the previous page. overriding function should be returned as true. When true is returned, it remains on the current xamarin form page, and the page state is also preserved.

0
source

Maybe this can be useful, you need to hide the back button and then replace it with your own button:

 public static UIViewController AddBackButton(this UIViewController controller, EventHandler ev){ controller.NavigationItem.HidesBackButton = true; var btn = new UIBarButtonItem(UIImage.FromFile("myIcon.png"), UIBarButtonItemStyle.Plain, ev); UIBarButtonItem[] items = new[] { btn }; controller.NavigationItem.LeftBarButtonItems = items; return controller; } public static UIViewController DeleteBack(this UIViewController controller) { controller.NavigationItem.LeftBarButtonItems = null; return controller; } 

Then call them in these methods:

 public override void ViewWillAppear(bool animated) { base.ViewWillAppear(animated); this.AddBackButton(DoSomething); UpdateFrames(); } public override void ViewWillDisappear(Boolean animated) { this.DeleteBackButton(); } public void DoSomething(object sender, EventArgs e) { //Do a barrel roll } 
0
source

Another way is to use Rg.Plugins.Popup, which allows you to implement a good popup. It uses another NavigationStack => Rg.Plugins.Popup.Services.PopupNavigation.Instance.PopupStack. This way, your page will not wrap around the navigation bar.

In your case, I would just

  • Create a popup with an opaque background
  • Override "OnBackButtonPressed for Android" with "ParentPage" something like this:

    protected override bool OnBackButtonPressed() { return Rg.Plugins.Popup.Services.PopupNavigation.Instance.PopupStack.Any(); }

Since the back button affects a normal NavigationStack, your parent will pop up whenever a user tries to use it during a pop-up window.

Now what? Xaml is all you want to properly close the popup with all the checks you want.

πŸ’₯ The problem is solved for these purposes

  • [x] Android
  • [x] iOS
  • [-] Windows Phone (deprecated. Use v1.1.0-pre5 if WP is required)
  • [x] UWP (min. job: 10.0.16299)
0
source

All Articles