之前在WP8的時候做過WebBrowser相關的筆記,在WP8.1的WebView和WebBrowser有些不一樣,在這里做一些筆記
下面分為幾個部分
1、禁止縮放
2、JS通知后台C#代碼(notify)
3、C#調用JS方法
動態加載JS文件,動態注冊事件方法(eval)
4、WebView導航
5、手勢(WinJS)
6、常見問題
1、禁用縮放
body { /* Block area from manipulation actions (zoom, pan) */ touch-action: pan-y; }
這個值可以禁用掉縮放和橫向手勢
關於touch-action參見:https://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh767313.aspx?f=255&MSPPError=-2147217396
2、JS通知后台C#代碼(notify)
window.external.notify(msg);
注意:這里的方法名是小寫的,在WP8上后面的Notify方法的首字母是大小寫都可以
3、C#調用JS方法(InvokeScriptAsync)
通過后台代碼動態加載css,js,動態綁定事件和方法
3.1、先定義傳輸的數據格式
/// <summary> /// JS消息格式 /// </summary> public class JsInvokeModel { [JsonProperty("type")] public string Type { get; set; } [JsonProperty("content1")] public string Content1 { get; set; } [JsonProperty("content2")] public string Content2 { get; set; } [JsonProperty("content3")] public string Content3 { get; set; } }
3.2、XAML
<WebView x:Name="WebView" DOMContentLoaded="WebView_OnDOMContentLoaded" ScriptNotify="WebView_OnScriptNotify" />
3.3、下面是事件方法
//DOM樹加載完成后執行 private async void WebView_OnDOMContentLoaded(WebView sender, WebViewDOMContentLoadedEventArgs args) { //1、動態加載css var js = @"var myCss = document.createElement(""link""); myCss.rel = ""stylesheet""; myCss.type = ""text/css""; myCss.href = ""ms-appx-web:///Assets/Html/css/default.css""; document.body.appendChild(myCss);"; await sender.InvokeScriptAsync("eval", new[] { js }); //2、動態加載js庫(json2) js = @"var myScript = document.createElement(""script""); myScript.type = ""text/javascript""; myScript.src = ""ms-appx-web:///Scripts/json2.min.js""; document.body.appendChild(myScript);"; await sender.InvokeScriptAsync("eval", new[] {js}); //3、調用js執行自定義代碼(為圖片添加點擊事件,並通知) js = @"var imgs = document.getElementsByTagName(""img""); for (var i = 0, len = imgs.length; i < len; i++) { imgs[i].onclick = function (e) { var jsonObj = { type: 'image', content1: this.src }; window.external.notify(JSON.stringify(jsonObj)); }; }"; await sender.InvokeScriptAsync("eval", new[] {js}); //4、動態加載手勢 js = @"var myScript = document.createElement(""script""); myScript.type = ""text/javascript""; myScript.src = ""ms-appx-web:///Assets/Html/js/gesture.js""; document.body.appendChild(myScript); window.external.notify(myScript.src+"""");"; await sender.InvokeScriptAsync("eval", new[] { js }); //5、為body添加手勢監聽 js = @"var target = document.getElementsByTagName(""body"")[0]; prepareTarget(target, eventListener);"; await sender.InvokeScriptAsync("eval", new[] { js }); }
3.4、Notify監聽
private void WebView_OnScriptNotify(object sender, NotifyEventArgs e) { //這個事件函數可以監聽到JS通知的消息,消息類型為文本 //這里統一消息格式為:JsInvokeModel var model = JsonConvert.DeserializeObject<JsInvokeModel>(e.Value); switch (model.Type) { case "image": Info.Text = e.Value; break; case "swiperight": //右滑 Info.Text = e.Value; break; case "swipeleft": //左滑 Info.Text = e.Value; break; case "text": Info.Text = e.Value; break; } }
WebView雖然提供了同步方法InvokeScript,但是在WP8.1沒有實現
通過InvokeScriptAsync,可以做更多操作,例如,相信對於更換顏色(夜間模式),修改字體大小等
4、WebView導航
兩種方式
//后退 //WebView.GoBack(); await WebView.InvokeScriptAsync("eval", new []{"history.go(-1)"}); //刷新 //WebView.Refresh(); await WebView.InvokeScriptAsync("eval", new[] { "history.go()" }); //前進 //WebView.GoForward(); await WebView.InvokeScriptAsync("eval", new[] { "history.go(1)" });
5、手勢
由於WebView的內部結構與WebBrowser不同,WebView無法監聽到Manipulation事件
場景:當我們需要在PivotItem中放置WebView的時候,左右滑動無法切換PivotItem,下面通過JS手勢監聽WebView上面的手勢操作,然后傳到后台代碼進行處理,這里沒有做實時處理,只是監聽了手勢離開時的速度判斷左右滑動
5.1、定義手勢監聽事件方法

var gesture; //記錄手勢操作開始位置 var gestureStartX; //觸發Id,防止重復觸發,觸發Id與手勢Id var gestureId = 1; var lastGestureId = 0; //速度觸發 var gestureVector = 1.5; //注冊手勢事件 function prepareTarget(target, eventListener) { //var target = document.getElementById(targetId); target.addEventListener("MSGestureStart", eventListener, false); target.addEventListener("MSGestureEnd", eventListener, false); target.addEventListener("MSGestureChange", eventListener, false); target.addEventListener("MSInertiaStart", eventListener, false); //target.addEventListener("MSGestureTap", eventListener, false); //target.addEventListener("MSGestureHold", eventListener, false); target.addEventListener("pointerdown", onPointDown, false); target.addEventListener("pointerup", onPointUp, false); gesture = new MSGesture(); gesture.target = target; } function onPointUp(e) { //把觸發時間參數傳到gesture gesture.addPointer(e.pointerId); } function onPointDown(e) { //把觸發時間參數傳到gesture gesture.addPointer(e.pointerId); } //手勢事件 //具體的屬性參見:https://msdn.microsoft.com/zh-cn/library/ie/hh772076%28v=vs.85%29.aspx function eventListener(evt) { var myGesture = evt.gestureObject; if (evt.type == "MSGestureStart") { //開始觸發,記錄初始位置 gestureStartX = evt.clientX; } else if (evt.type == "MSInertiaStart") { if (lastGestureId == gestureId || evt.velocityX == "undefined") { return; } else { //釋放時觸發慣性事件,判斷手勢釋放時的速度 if (evt.velocityX > gestureVector) { var jsonObj = { type: "swiperight" }; window.external.notify(JSON.stringify(jsonObj)); lastGestureId = gestureId; } else if (evt.velocityX < -gestureVector) { jsonObj = { type: "swipeleft" }; window.external.notify(JSON.stringify(jsonObj)); lastGestureId = gestureId; } } } else if (evt.type == "MSGestureChange") { //if (lastGestureId == gestureId) { // return; //} else { // var change = evt.clientX - gestureStartX; // window.external.notify("clientX:" + change); //} } else if (evt.type == "MSGestureEnd") { //手勢結束,Id+1 gestureId = gestureId + 1; myGesture.reset(); } }
5.2、在WebView加載完成后,動態加載改JS文件,獲取body標簽,然后監聽事件(參見3.3)
5.3、當事件觸發的時候改變Pivot.SelectedIndex,這樣就能實現在WebView上滑動切換PivotItem
private void WebView_OnScriptNotify(object sender, NotifyEventArgs e) { //這個事件函數可以監聽到JS通知的消息,消息類型為文本 //這里統一消息格式為:JsInvokeModel var model = JsonConvert.DeserializeObject<JsInvokeModel>(e.Value); switch (model.Type) { case "image": Info.Text = e.Value; break; case "swiperight": Info.Text = e.Value; if (pivot.Items != null) { if (pivot.SelectedIndex > 0) { pivot.SelectedIndex--; } else { pivot.SelectedIndex = pivot.Items.Count - 1; } } break; case "swipeleft": Info.Text = e.Value; if (pivot.Items != null) { if (pivot.SelectedIndex < pivot.Items.Count - 1) { pivot.SelectedIndex++; } else { pivot.SelectedIndex = 0; } } break; case "text": Info.Text = e.Value; break; } }
6、常見問題
6.1、alert和prompt方法在WebView失效,如果需要,可以使用 window.external.notify('message');然后再后台代碼進行處理
6.2、如果是導航到本地路徑(ms-appdata:///local/),需要注意
6.2.1、必須要將html需要的資源和html文件放在同一個文件夾下,html的資源路徑不管是不是”/”開頭,webview都認為是相對路徑
6.2.2、如果html中要的本地文件,如果在對應目錄中不存在,wp上會直接跳出應用,大概過1分鍾左右崩潰,且捕獲不到異常。Windows上沒有這現象
6.2.3、不支持js的window.exteranl.notify功能
webView1.Navigate(new Uri("ms-appdata:///local/data/html/sample.htm"));
不知道為什么WebView在導航到本地路徑的時候 window.external.notify 會失效,如果需要可以考慮使用 內容流的方式加載本地文件
下面做簡單的演示
1、定義內容留轉換器

public class HtmlStreamUriResolver : IUriToStreamResolver { public IAsyncOperation<IInputStream> UriToStreamAsync(Uri uri) { string path = uri.AbsolutePath; return GetContent(path).AsAsyncOperation(); } // 根據 uri 返回對應的內容流 private async Task<IInputStream> GetContent(string path) { if (path.StartsWith("http")) { // http 方式獲取內容數據 var client = new HttpClient(); var response = await client.GetAsync(new Uri(path), HttpCompletionOption.ResponseHeadersRead); return (await response.Content.ReadAsStreamAsync()).AsInputStream(); } else if (path.StartsWith("/local/cachedata/html/")) { path = string.Format("ms-appdata://{0}", path); // 獲取本地數據 var fileRead = await StorageFile.GetFileFromApplicationUriAsync(new Uri(path)); return await fileRead.OpenAsync(FileAccessMode.Read); } else { // 獲取本地數據 var fileRead = await StorageFile.GetFileFromApplicationUriAsync(new Uri(path, UriKind.Absolute)); return await fileRead.OpenAsync(FileAccessMode.Read); } } }
2、生成,導航,html中的所有引用都通過HtmlStreamUriResolver返回,具體邏輯在里面處理,通過這樣導航的頁面可以使用 window.external.notify 方法
//生成內容流 var htmlStreamUriResolver = new HtmlStreamUriResolver(); var uri = webView.BuildLocalStreamUri("tag", "/local/cachedata/html/sample.html"); webView.NavigateToLocalStreamUri(uri, htmlStreamUriResolver);
7、Demo
http://files.cnblogs.com/files/bomo/WebViewDemo.zip
8、參考鏈接
手勢事件參數說明:https://msdn.microsoft.com/zh-cn/library/ie/hh772076%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396
WinJS手勢:https://msdn.microsoft.com/zh-cn/library/ie/hh968249(v=vs.85).aspx
Win8.1和WP8.1在UniversalAPP中WebView的研究:http://blog.csdn.net/hzdinglai/article/details/41073739
重新想象 Windows 8.1 Store Apps (81) : WebView:http://www.cnblogs.com/webabcd/p/3803384.html
關於WP8的WebBrowser,請移步:http://www.cnblogs.com/bomo/p/3949994.html
個人能力有限,如果上文有誤或者您有更好的實現,可以給我留言