如何重置Prism for Xamarin.Forms(视图)导航堆栈?

3

我在解决Prism.Forms(v6.2.0)和Xamarin消息插件(Xam.Plugins.Messaging v3.2.1)之间的冲突,当我尝试从我的应用程序发送电子邮件时。

以下代码是从我的一个ViewModel执行的:

using Plugin.Messaging;

if (MessagingPlugin.EmailMessenger.CanSendEmail)
{
    var email = new EmailMessageBuilder()
        .To("test@testemail.com")
        .Subject("Email subject here!")
        .Body("Dear friend,\nI would like to say...\n\n\n")
        .Build();
    MessagingPlugin.EmailMessenger.SendEmail(email);
}

产生此错误,且未在屏幕上显示即将发送的电子邮件:

警告︰ 试图在视窗层次结构中不包含其视图的 <MFMailComposeViewController: 0x108fb4400> 上呈现。 <Xamarin_Forms_Platform_iOS_PageRenderer: 0x107de67c0>

我认为这是因为消息插件的iOS特定部分有一个.GetVisibleViewController()方法,用于查找具有可见视图的UIViewController以显示即将发送的电子邮件;并且它正在找到iOS不喜欢的一个视图控制器。
我想指出的是,我几乎完全相同的代码(如上所示)在使用Prism.Forms的MVVM模式Xamarin.Forms应用程序中运行而没有问题[编辑:Brian L.指出这个问题可能不是由Prism引起的,并且我倾向于同意他;尽管电子邮件构建/发送代码相同,但两个应用程序之间的“导航流”存在细微差异,更有可能与此相关]。当MessagingPlugin.EmailMessenger.SendEmail(email);线路执行时,应该弹出一个电子邮件,其中包含上述代码中显示的收件人,主题和正文;并带有发送或取消按钮。在非Prism Xamarin.Forms应用程序中,这有效地工作。
因此,我已经解决了这个问题,使.SendEmail(email)方法调用正常工作。我必须将我的X.F页面(即我的视图模型的视图)传递给iOS特定项目代码,以在其上调用.CreateViewController()。我使用此新UIViewController创建一个UIWindow,并使该窗口.MakeKeyAndVisible()。我调整了Xam.Plugins.Messaging的代码,以让我传递一个要使用的UIViewController。在.SendEmail(email)方法调用发生后,我会处理我新创建的UIWindow;这样,在电子邮件发送或取消后,我不会看到它。
这一切都可以正常工作,并允许我像预期的那样使用消息插件的电子邮件发送功能。
我唯一剩下的问题是,在发送电子邮件后,对我的原始视图模型的navigationService实例(即尝试使用上面的代码发送电子邮件的ViewModel)进行导航调用失败。在发送电子邮件后,我的视图/视图模型上的其他功能都能正常工作;但是调用navigationService.GoBackAsync()navigationService.NavigateAsync()都会失败,并产生一个熟悉的错误:

警告︰ 试图在视窗层次结构中不包含其视图的 <Xamarin_Forms_Platform_iOS_ModalWrapper: 0x1359c21a0> 上呈现。 <Xamarin_Forms_Platform_iOS_PlatformRenderer: 0x133df9640>

我认为我想做的是重置我的Prism NavigationService(视图)堆栈,类似于以下代码(用于Xamarin.Forms,不使用Prism):
Navigation.InsertPageBefore(new HomePage(), Navigation.NavigationStack.First());
Navigation.PopToRootAsync(true);

但是我并没有找到使用Prism NavigationService来进行"导航堆栈修改操作"的方法,也许其他人有这方面的经验吗?提前感谢您的帮助。
1个回答

14
我有一段几乎相同的代码(与上面显示的代码类似),在一个没有使用Prism.Forms的MVVM模式的Xamarin.Forms应用程序中运行良好。
几乎相同并不等同。我想提到的是,Prism在MVVM方面并没有做任何特殊的事情。它只是为您设置了Page.BindingContext。就这样。因此,如果您在没有Prism的应用程序中仅使用View和VM工作,则使用Prism没有任何区别。
回答您的问题,要重置导航堆栈,您只需使用绝对URI。
NavigateAsync("myapp:///NavigationPage/MainPage")
这相当于MainPage = new NavigationPage(new MainPage());

谢谢Brian,我会尝试的! - Jeremy Ellis
3
谢谢你,Brian。这解决了我的问题。现在我不再使用navigationService.NavigateAsync("HomePage"),而是使用navigationService.NavigateAsync("app:///HomePage") - 一切正常工作。我查找了有关Prism绝对URI的文档,但我不清楚你上面示例中的“myapp”是否为“神奇字符串”,或者需要根据我的应用程序进行自定义。但是似乎可以使用“anystring:///HomePage”仍然可以工作。如果我错了,请告诉我。如果这有助于未来的读者解决类似问题,那就太好了。 - Jeremy Ellis
1
这是正确的。基本上,你所需要做的就是提供一个有效的绝对 URI。这可以是任何东西,从http://www.myapp.com/MainPage到创建一个实际的new Uri("some uri", UriKind.Absolute) - user5420778
@BrianLagunas 感谢您提供的解决方案和解释,让我受益匪浅。我还有两个疑问,比如说我从页面A导航到B、C、D,然后在D中的某个操作中,如果我只需要删除页面C,我该怎么做?如果我想处理Android设备的返回按钮,并使其按照我的返回按钮功能工作,我该怎么做?我已经在网上搜索了很多天,但是返回导航对我来说是一个巨大的问题。您能帮帮我吗? - ROHIT MANDALAPU
如果有后台任务,它们不会被取消并将继续执行。因此,先前的堆栈不会被销毁 :) - Stefanija

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接