web-dev-qa-db-fra.com

Comment gérer/annuler la navigation de retour dans Xamarin Forms

J'ai essayé d'utiliser la navigation arrière en surchargeant OnBackButtonPressed, mais d'une manière ou d'une autre, l'appel n'a pas été appelé. J'utilise la ContentPage et la dernière version 1.4.2. 

13
imgen

D'accord, après plusieurs heures, j'ai compris celui-ci. Il comporte trois parties.

# 1 Gestion du bouton de retour du matériel sous Android. Celui-ci est facile, remplacez OnBackButtonPressed. Rappelez-vous, ceci est pour un bouton de retour matériel et Android seulement. Il ne gérera pas le bouton retour de la barre de navigation. Comme vous pouvez le constater, j’essayais de revenir en arrière dans un navigateur avant de quitter la page, mais vous pouvez y insérer la logique dont vous avez besoin.

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

N ° 2 bouton de navigation arrière iOS. Celui-ci était vraiment délicat. Si vous parcourez le Web, vous trouverez quelques exemples de remplacement du bouton Précédent par un nouveau bouton personnalisé, mais il est presque impossible de le faire ressembler à vos autres pages. Dans ce cas, j'ai créé un bouton transparent qui repose sur le bouton normal.

[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, c'est délicat. Dans les anciennes versions et les versions futures des formulaires, une fois corrigés, vous pouvez simplement remplacer le paramètre OnOptionsItems sélectionné comme suit.

       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);
        }
    }

Toutefois, si vous utilisez FormsAppCompatActivity, vous devez ajouter à votre OnCreate dans MainActivity ceci pour définir votre barre d’outils:

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

Mais attendez! Si vous avez une version trop ancienne de .Forms ou une version trop récente, un bogue surviendra lorsque la barre d’outils est nulle. Si cela se produit, la façon dont je l'ai fait fonctionner pour fixer un délai est comme ça. Dans OnCreate dans 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 est une page et androidAction est une Action que je lance sur OnAppearing si la plate-forme est Android sur ma page. À ce stade de votre application, la barre d’outils ne sera plus nulle.

Quelques étapes supplémentaires, le rendu iOS que nous avons créé ci-dessus utilise les propriétés que vous devez ajouter à la page pour laquelle vous créez le rendu. Je le fabriquais pour ma classe MyAdvantagePage que j'ai créée, qui implémente ContentPage. Donc, dans ma classe MyAdvantagePage j'ai ajouté

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);
            }
        }

Maintenant que tout cela est fait, je peux ajouter ceci sur n'importe laquelle de mes MyAdvantagePage

:


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

Cela devrait suffire à le rendre opérationnel sur le matériel Android, et à la navigation pour Android et iOS. 

14
Kyle

Vous avez raison, dans votre classe de page, remplacez OnBackButtonPressed et renvoyez true si vous souhaitez empêcher la navigation. Cela fonctionne bien pour moi et j'ai la même version.

protected override bool OnBackButtonPressed()
{
    if (Condition)
        return true;
    return base.OnBackButtonPressed();
}
11

En fonction de ce que vous recherchez exactement (je ne recommanderais pas cette option si vous souhaitez simplement annuler la navigation avec le bouton Précédent), OnDisappearing peut être une autre option:

protected override void OnDisappearing()
{
       //back button logic here
}
2
Alexander Kohler

OnBackButtonPressed () Ceci sera appelé quand un bouton de retour matériel est enfoncé comme dans Android. Cela ne fonctionnera pas si vous appuyez sur le bouton retour du logiciel comme dans ios.

1
Anshu

L'astuce consiste à implémenter votre propre page de navigation qui hérite de NavigationPage. Il contient les événements appropriés Pushed, Popped et PoppedToRoot.

Un exemple d'implémentation pourrait ressembler à ceci:

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;
    }
}

Les pages implémenteraient l'interface suivante:

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

Cette interface peut être implémentée dans une classe de base pour toutes les pages, puis déléguer ses appels à son modèle de vue.

La page de navigation et pourrait être créé comme ceci:

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

MainPage serait la page racine à afficher.

Bien sûr, vous pouvez aussi simplement utiliser NavigationPage et vous abonner à ses événements sans en hériter.

1
lauxjpn

Pour tous ceux qui luttent encore contre ce problème, vous ne pouvez pas intercepter la navigation sur plusieurs plates-formes. Cela dit, deux approches permettent de résoudre efficacement le problème:

  1. Masquer le bouton Précédent de NavigationPage avec NavigationPage.ShowHasBackButton(this, false) et afficher une page modale dotée d'un bouton Précédent/Annuler/Fermer personnalisé

  2. Intercepter la navigation arrière en mode natif pour chaque plate-forme. C'est un bon article qui le fait pour iOS et Android: https://theconfuzedsourcecode.wordpress.com/2017/03/12/lets-override-navigation-bar-back-button-click-in-xamarin-forms/

Pour UWP, vous êtes seul :)

Modifier:

Eh bien, plus maintenant que je l’ai fait :) En fait, cela s’est avéré très simple: il n’ya qu’un bouton précédent, pris en charge par Forms. Il vous suffit donc de remplacer le OnBackButtonPressed de ContentPage:

    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
AXE
 protected override bool OnBackButtonPressed()
        {
            base.OnBackButtonPressed();
                return true;
        }

base.OnBackButtonPressed() renvoie false lorsque vous cliquez sur le bouton de retour du matériel. Afin d’empêcher l’utilisation du bouton Précédent ou d’empêcher la navigation vers la page précédente. la fonction prioritaire doit être retournée comme étant vraie. Sur return true, il reste sur la page de formulaire xamarin actuelle et son état est également conservé.

0
HARSHITHA LAKSHMI

Peut-être que cela peut être utile, vous devez cacher le bouton de retour, puis le remplacer par votre propre bouton:

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;
}

Puis appelez-les dans ces méthodes:

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
El0din