我想制作一个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”参数没有任何作用。
答案 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"); }
}
}