C# 在Xamarin表单的主/详细布局中从当前页面切换时,保留WebView加载的页面内容

C# 在Xamarin表单的主/详细布局中从当前页面切换时,保留WebView加载的页面内容,c#,xamarin,webview,xamarin.ios,xamarin.forms,C#,Xamarin,Webview,Xamarin.ios,Xamarin.forms,我正在使用Master/Detaillayout构建一个导航菜单和一些页面。其中一个页面是WebView控件,没有其他内容。当我从导航菜单中切换离开这个WebView页面,然后再切换回来时,WebView的内容消失了,状态也消失了。这在iOS和Android上都会发生 但如果我从WebView页面导航到一个完全不同的页面(非主控/详细页面),在返回WebView页面后,一切都很好。将保留所有内容和状态 我必须重新加载页面,但用户的操作将丢失。是否有办法保留页面的内容和状态,并在不重新加载页面的情

我正在使用
Master/Detail
layout构建一个导航菜单和一些页面。其中一个页面是
WebView
控件,没有其他内容。当我从导航菜单中切换离开这个WebView页面,然后再切换回来时,WebView的内容消失了,状态也消失了。这在
iOS
Android
上都会发生

但如果我从WebView页面导航到一个完全不同的页面(非主控/详细页面),在返回WebView页面后,一切都很好。将保留所有内容和状态


我必须重新加载页面,但用户的操作将丢失。是否有办法保留页面的内容和状态,并在不重新加载页面的情况下还原它们

导航控制器将现有UIViewController保留在导航堆栈上,但当您弹出UIViewController时,它会从堆栈中删除视图控制器,因此其内容会被删除

要保留它,您需要有一个WebViewController的共享实例,并且始终使用相同的WebViewController

在WebView控制器中,编写以下代码以创建共享实例:

private static ViewController vc;

public static WebViewController SharedInstance()
{
    if (vc == null)
    {
        var storyboard = UIStoryboard.FromName("MainStoryboard", null);
        vc = storyboard.InstantiateViewController("WevViewController") as WevViewController;
    }

    return vc;
}
每当您想要显示web视图控制器时,请使用以下命令

public void DisplayWebview()
{
    WevViewController webvw = WevViewController.SharedInstance();
    NavigationController.PushViewController(webvw,true);
}

App
类中,创建包含
WebView
ContentPage
实例。在
App
类中将
ContentPage
创建为静态变量将保留其状态,并确保其打开到用户最近查看的同一网页

示例代码

不幸的是,您需要修改以提供所需的行为。问题是,当细节页面从MasterDetail中移除(从Webview切换到另一个页面)时,所有本机控件都被释放;即使您硬引用
Xamarin.Forms.Webview
Xamarin.Forms.Page
对象

在源代码中,您可以看到,如果
\u detail
属性不为null,则它将从
MasterDetailPage.PageController.InternalChildren
中删除。您需要在那里签入包含
Xamarin.Forms.Webview
Xamarin.Forms.Page
,并确保未将其从页面控制器中删除。这将保留本机webview和webview处理程序,因此不会重新加载任何内容。需要注意的几点:

  • 确保您也没有将webview页面重新添加到MasterDetail
    PageController.InternalChildren
  • webview页面将保持加载到内存中。这一点很重要,因为如果您的网站使用大量javascript、存储内容或更多内容,那么您应该知道,即使没有显示,它也会保留在内存中
  • 您可以通过创建一个新的表单项目并用以下内容替换
    App
    类代码来验证所有这些:

    public class App : Application
    {
        public App()
        {
            MainPage = new MasterDetail();
        }
    }
    
    public class MasterDetail : MasterDetailPage
    {
        static WebViewPage persistentWebPage = new WebViewPage();
    
        public MasterDetail()
        {
            var masterPage = new MasterPage();
            persistentWebPage = new WebViewPage();
    
            Master = masterPage;
            Detail = persistentWebPage;
    
            masterPage.listview.ItemSelected += (sender, e) =>
            {
                var item = e.SelectedItem as string;
    
                if (string.IsNullOrEmpty(item))
                    return;
    
                switch (item)
                {
                    case "1":
                        Detail = persistentWebPage;
                        break;
                    default:
                        Detail = new ContentPage { BackgroundColor = Color.Red };
                        break;
                }
    
                masterPage.listview.SelectedItem = null;
                IsPresented = false;
            };
        }
    }
    
    public class MasterPage : ContentPage
    {
        public ListView listview = new ListView { ItemsSource = new string[] { "1", "2", "3" } };
    
        public MasterPage()
        {
            Title = "Master";
            Content = listview;
        }
    }
    
    public class WebViewPage : ContentPage
    {
        public static WebView webView = new WebView { Source = "https://www.google.com/" };
    
        public WebViewPage()
        {
            Content = webView;
        }
    }
    
    接下来,在Android项目中创建一个渲染器(您也可以使用iOS,我只是使用Android)并复制以下代码:

    public class WebViewRenderer_Droid : WebViewRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.WebView> e)
        {
            if (e.NewElement == null)
                Console.WriteLine("*********e.NewElement is null");
            else
                Console.WriteLine("*********e.NewElement is not null");
    
            if (e.OldElement == null)
                Console.WriteLine("*********e.OldElement is null");
            else
                Console.WriteLine("*********e.OldElement is not null");
    
            if (Control == null)
                Console.WriteLine("*********Control is null");
            else
                Console.WriteLine("*********Control is not null");
    
            base.OnElementChanged(e);
        }
    }
    
    公共类WebViewRenderer\u Droid:WebViewRenderer
    {
    受保护的覆盖无效OnElementChanged(ElementChangedEventArgs e)
    {
    if(e.NewElement==null)
    Console.WriteLine(“*********e.NewElement为空”);
    其他的
    Console.WriteLine(“*********e.NewElement不为空”);
    if(e.OldElement==null)
    Console.WriteLine(“******e.OldElement为空”);
    其他的
    Console.WriteLine(“*********e.OldElement不为空”);
    if(Control==null)
    Console.WriteLine(“*********控件为空”);
    其他的
    Console.WriteLine(“*********控件不为空”);
    基础。一个要素发生变化(e);
    }
    }
    
    运行该项目并从webview页面切换回来。您将看到
    Control
    e.OldElement
    在一个点上都为null。这证明即使使用硬引用,也会处理本机处理程序。这是因为您无法控制Xamarin.Forms如何处理本机控件引用,我们可以看到,如果
    Control
    为null,那么所有内容都将被重新创建


    我希望这有帮助!深入挖掘来找出这一点很有趣

    我找到了一个有效的解决方案,尽管更像是一个黑客。也就是说,当触摸一个菜单项时,我没有导航到主控/详细信息中的子页面,而是导航到另一个页面,这意味着向下1级。当用户从该页面返回时,所有内容都将被保留。但这样一来,导航菜单就不再总是在那里了。不过没关系

    为了解决这个问题,我创建了一个自定义webview渲染器。当我在渲染器中创建新的webview时(例如,new WKWebView(Frame,opts)),我将其替换为覆盖Dispose的WKWebView子类,使其不做任何操作。根据需要跟踪OnElementChanged中的原始webview。

    您需要在OnAppearing()方法中设置webview的内容或来源否,这正是我不想做的,因为我只想加载一次页面,并在导航离开该页面后将内容保留在那里,然后再导航回来。为此,您可以在页面的override方法OnDisappearing()上将webview的源设置为“about:blank”,确定吗?由于将其设置为大约:空白,因此明确销毁上一页。我不想破坏页面,我想保存页面状态查看我的帖子,你必须修改MasterDetail的源代码以保留本机WebView谢谢你的回答,但我使用的是Xamarin Forms,它是Android/iOS api的包装器,您的答案是关于本机iOS api。似乎无法使用母版/详细页导航它似乎保存了webView的URL,但仍在重新加载该URL。重新加载就像重新访问pageHi,我想你找到了原因,但解决方案显然比你建议的要多
    public class WebViewRenderer_Droid : WebViewRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.WebView> e)
        {
            if (e.NewElement == null)
                Console.WriteLine("*********e.NewElement is null");
            else
                Console.WriteLine("*********e.NewElement is not null");
    
            if (e.OldElement == null)
                Console.WriteLine("*********e.OldElement is null");
            else
                Console.WriteLine("*********e.OldElement is not null");
    
            if (Control == null)
                Console.WriteLine("*********Control is null");
            else
                Console.WriteLine("*********Control is not null");
    
            base.OnElementChanged(e);
        }
    }