如何在Xamarin Forms中处理/取消后退导航

时间:2015-06-05 02:20:19

标签: xamarin navigation xamarin.forms

我尝试通过覆盖OnBackButtonPressed来使用后退导航,但不知何故它根本没有被调用。我正在使用ContentPage和最新的1.4.2版本。

11 个答案:

答案 0 :(得分:16)

好吧,经过几个小时,我想出了这个。它有三个部分。

#1处理android上的硬件返回按钮。这个很容易,覆盖OnBackButtonPressed。请记住,这仅适用于硬件后退按钮和Android。它不会处理导航栏后退按钮。正如您所看到的,我在退出页面之前尝试通过浏览器进行备份,但您可以放置​​所需的逻辑。

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

#2 iOS导航后退按钮。这个非常棘手,如果你环顾网络,你会发现一些用新的自定义按钮替换后退按钮的例子,但几乎不可能让它看起来像你的其他页面。在这种情况下,我制作了一个位于普通按钮顶部的透明按钮。

[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,很棘手。在旧版本和未来版本的Forms中修复后,您可以简单地覆盖OnOptionsItemselected,如此

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

但是,如果您使用的是FormsAppCompatActivity,则需要在MainActivity中添加OnCreate来设置工具栏:

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

但是等等!如果您的.Forms版本太旧或太新版本,则工具栏为空时会出现错误。如果发生这种情况,那么我将其用于制定截止日期的黑客攻击方式是这样的。 OnCreate中的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是一个Page,而androidAction是我在OnAppearing上运行的Action,如果我的页面上的平台是Android的话。在您的应用中,此时工具栏将不再为空。

更多步骤,我们上面制作的iOS渲染使用您需要添加到渲染器的任何页面的属性。我正在为我创建的MyAdvantagePage类创建它,它实现了ContentPage。所以在我的MyAdvantagePage课程中我添加了

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

现在这一切都已完成,在我MyAdvantagePage的任何一个上我都可以添加

:


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

这应该是让它在Android硬件上运行的一切,并为Android和iOS导航。

答案 1 :(得分:12)

您是对的,在您的网页类中覆盖OnBackButtonPressed,如果您想阻止导航,请返回true。它适用于我,我有相同的版本。

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

答案 2 :(得分:2)

根据您的确切需求(如果您只想取消后退按钮导航,我建议不要使用此功能),OnDisappearing可能是另一种选择:

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

答案 3 :(得分:1)

OnBackButtonPressed()在按下硬件后退按钮时会调用它,就像在android中一样。 这不适用于软件后退按钮,如ios。

答案 4 :(得分:1)

对于仍在与此问题作斗争的人 - 基本上你无法拦截跨平台的导航。说过有两种方法可以有效地解决问题:

  1. 使用NavigationPage.ShowHasBackButton(this, false)隐藏NavigationPage后退按钮并按下具有自定义后退/取消/关闭按钮的模态页面

  2. 本地拦截每个平台的后退导航。这篇文章适用于iOS和Android:https://theconfuzedsourcecode.wordpress.com/2017/03/12/lets-override-navigation-bar-back-button-click-in-xamarin-forms/

  3. 对于UWP,你可以自己:)

    编辑:

    好了,自从我做了以后就不再了:)它实际上变得非常简单 - 只有一个后退按钮,它受到Forms支持,所以你只需要覆盖ContentPage的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);
            }           
        }
    

答案 5 :(得分:1)

诀窍是实现自己的导航页面,该页面继承自NavigationPage。它具有相应的事件PushedPoppedPoppedToRoot

示例实现可能如下所示:

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将实现以下界面:

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

此界面可以在所有页面的基类中实现,然后将其调用委托给它的视图模型。

导航页面可以像这样创建:

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

MainPage将成为显示的根页。

当然,你也可以首先使用NavigationPage并订阅它的事件而不继承它。

答案 6 :(得分:1)

除Kyle答案外 设置

在您的页面内部

public static Action SetToolbar;

出现您的页面

if (Device.RuntimePlatform == Device.Android)
{
    SetToolbar.Invoke();
}

MainActivity

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

答案 7 :(得分:1)

我使用Prism libray,并为处理后退按钮/操作,在页面上扩展了Prism的INavigatedAware接口,并实现了以下方法:

    public void OnNavigatedFrom(INavigationParameters parameters)
    {
        if (parameters.GetNavigationMode() == NavigationMode.Back)
        {
            //Your code
        }
    }

    public void OnNavigatedTo(INavigationParameters parameters)
    {
    }

当用户按下导航栏上的“后退”按钮(Android和iOS)并且当用户按下“硬件后退”按钮(仅适用于Android)时,将引发OnNavigatedFrom方法。

答案 8 :(得分:0)

 protected override bool OnBackButtonPressed()
        {
            base.OnBackButtonPressed();
                return true;
        }
单击硬件后退按钮时,

base.OnBackButtonPressed()返回false。 为了防止后退按钮操作或阻止导航到上一页。覆盖函数应返回true。返回true时,它将保留在当前的xamarin表单页面上,并且还会保持页面状态。

答案 9 :(得分:0)

也许这可能有用,您需要隐藏“后退”按钮,然后用自己的按钮替换:

ObjectB

然后将它们调用为以下方法:

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

答案 10 :(得分:0)

另一种解决方法是使用Rg.Plugins.Popup,它允许您实现漂亮的弹出窗口。它使用另一个NavigationStack => Rg.Plugins.Popup.Services.PopupNavigation.Instance.PopupStack。这样您的页面就不会被NavigationBar环绕了。

就您而言,我会简单地

  • 创建不透明背景的全页弹出窗口
  • 使用类似这样的方法在⚠️ParentPage⚠️上覆盖↩️OnBackButtonPress for Android:

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

由于后退按钮会影响通常的NavigationStack,因此当您的“弹出窗口正在显示”时,只要用户尝试使用它,父项就会弹出。

现在呢? Xaml无论您想要什么,都可以使用所需的所有支票来正确关闭弹出窗口。

?这些目标已解决问题?

  • [x] Android
  • [x] iOS
  • [-] Windows Phone(已淘汰。如果需要WP,请使用v1.1.0-pre5)
  • [x] UWP(最低目标:10.0.16299)