Xamarin HybridWebView导航到缓存文件夹中的页面

时间:2018-11-04 10:22:30

标签: c# xamarin.forms uwp

我想制作一个xamarin表单的Webview应用程序,该应用程序下载一个网页(该应用程序本身)然后显示它。在下载之前,我显示了一个本地登录页面,该页面将在用户提交表单后调用该应用程序。本地页面将位于应用程序包内,下载的页面将下载至应用程序可以存储数据的位置(此uwp示例中为CommenApplicationData,但我必须使用iOS上的documents文件夹)。

我已经制作了一个.NET Core库,该库可以下载,解压缩并支持webview内的网页使用的脚本命令。该库的主要入口类称为:BroadsheetInterface,它接受来自Webview的消息(BroadsheetInterface.RunCommand),并使用接口对Webview(RunJavascript)进行回调。

要显示本地的“登录页面”并下载“应用程序页面”或“启动页面”,我使用Xamarin中的HybridWebView示例。然后,我修改了特定于平台的hybridwebview来进行自己的导航,如下所示:

// IWebView is a interface that is used for the BroadsheetInterface can do callbacks 
// to this control
public class HybridWebViewRenderer : ViewRenderer<HybridWebView, Windows.UI.Xaml.Controls.WebView>, IWebView 
{
    // The pointer to the current "BroadsheetInterface", a class that has all logic of 
    // the webpage inside the webview
    public BroadsheetInterface_Helper BroadsheetInterface { get; set; }

    // The standard call to setup the webview to a local page, I use this call to show 
    // the login page.
    protected override void OnElementChanged(ElementChangedEventArgs<HybridWebView> e)
    {
        base.OnElementChanged(e);

        if (Control == null)
        {
            SetNativeControl(new Windows.UI.Xaml.Controls.WebView());
        }
        if (e.OldElement != null)
        {
            Control.NavigationCompleted -= OnWebViewNavigationCompleted;
            Control.ScriptNotify -= OnWebViewScriptNotify;
        }
        if (e.NewElement != null)
        {
            Control.NavigationCompleted += OnWebViewNavigationCompleted;
            Control.ScriptNotify += OnWebViewScriptNotify;

            LoadLoginPage();                
        }
    }

    // Once the local page has been loaded I setup the library (note the "this" 
    // parameter), then check if the user has checked the "remember login" checkbox 
    // last time he or she logged in.
    void OnWebViewNavigationCompleted(WebView sender, WebViewNavigationCompletedEventArgs args)
    {
        if (args.IsSuccess)
        {
            BroadsheetInterface = new BroadsheetInterface_Helper(this);
            BroadsheetInterface.Browser_SetLogin();
        }
    }

    // If the Javascript inside the webview calls the app, the command will be passed 
    // into the BroadsheetInterface to be processed. 
    void OnWebViewScriptNotify(object sender, NotifyEventArgs e)
    {
        BroadsheetInterface.RunCommand(e.Value);
    }

    // If the library want to call the webpage inside the webview, it will call 
    // this function
    public async void RunJavascript(string script)
    {
        await Control.InvokeScriptAsync("eval", new[] { script });
    }

    // Show the login page
    public void LoadLoginPage()
    {
        Control.Source = new Uri(string.Format("ms-appx-web:///Content//{0}", Element.Uri));
    }

    // Show the launcher page
    public void LoadLauncherPage()
    {
        // The location of the launcher page
        var fullname = BroadsheetInterface.AccountHelper.CurrentAccount.GetLauncherPage().FullName;
        // fullname = "C:\Users\wille\AppData\Local\Packages\a05728a3-841f-4ef2-8f13-f1dbf39590a0_tbz3402trp7yy\LocalState\ProgramData\Launcher\willem\index.html"

        // Convert the path to a url
        var url = "file:///" + fullname.Replace("\\", "/");
        // url = "file:///C:/Users/wille/AppData/Local/Packages/a05728a3-841f-4ef2-8f13-f1dbf39590a0_tbz3402trp7yy/LocalState/ProgramData/Launcher/willem/index.html"

        // Set it to the webview
        Control.Source = new Uri(url);
        // DOESN'T WORK :(
    }

    // This is called from the library to get the location where to store 
    // the launcher page
    public string GetCacheFolder()
    {
        return Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
    }
}

现在该应用程序可以运行了,我可以登录了,提交后,它开始下载文件。下载文件后,我要设置Web视图以显示已下载的启动器页面。因此,我调用了“ LoadLauncherPage()”函数,但是它不会更改为下载的页面。就像设置“ Source”参数没有任何作用。

1 个答案:

答案 0 :(得分:0)

好,我自己发现了。

我猜不允许将WebView的Source属性设置为“ file:///” URL。所以我遇到了这个解决方案:

// Show the launcher page
public void LoadLauncherPage()
{
    Uri uri = Control.BuildLocalStreamUri("Launcher", "/index.html");
    Control.NavigateToLocalStreamUri(uri, new StreamResolver(BroadsheetInterface));
}

// Resolver for mapping the files
public class StreamResolver : IUriToStreamResolver
{
    private BroadsheetInterface_Helper broadsheetInterface;

    public StreamResolver(BroadsheetInterface_Helper broadsheetInterface)
    {
        this.broadsheetInterface = broadsheetInterface;
    }

    public IAsyncOperation<IInputStream> UriToStreamAsync(Uri uri)
    {
        if (uri == null) throw new Exception();
        return GetContent(uri.AbsolutePath).AsAsyncOperation();
    }
    private async Task<IInputStream> GetContent(string path)
    {
        try
        {
            string fullname = broadsheetInterface.AccountHelper.CurrentAccount.RootFolder.FullName + path.Replace("/", "\\");
            StorageFile f = await StorageFile.GetFileFromPathAsync(fullname);
            IRandomAccessStream stream = await f.OpenAsync(FileAccessMode.Read);
            return stream;
        }
        catch (Exception) { throw new Exception("Invalid path"); }
    }
}