在 WebView 裏面讓 Javascript 與 App 互動已經是很平常的事情了,爲什麽要特別寫這篇。 因爲發現除了 window.external.notify 允許從網頁送入訊息到 App 之外,還可以加入自定義的 component 到網頁裏讓 Javascript 使用。 這篇將介紹怎麽實作。
根據 WebView class 的介紹,在 Windows 10 開始允許利用 AddWebAllowedObject method 去引用建立好的 Windows Runtime component 到 WebView 的 Javascript context 之中,讓 Javascript 有能力存取 native 的 properties, methods, events。
那我們來看要做些什麽才能完成這樣的功能。
1. 建立 Universal Windows Platform 與 Windows Runtime component ,再將 Component 的專案加入到 App 的參考之中;
2. 為 component 專案中的 class 加入 AllowForWeb 的 attribute;
AllowForWebAttribute Class 允許讓開發者公開 native 物件變成類似 global parameter 的内容放在 WebView 的 top-level。
由於宣告 AllowForWeb 的類別是受到保護的,所以需要是 sealed 的 class。 如下的程式碼:
[AllowForWeb]
public sealed class JavaScriptExternalObject
{
/// <summary>
/// 提供給外部使用時收聽的事件
/// </summary>
public event EventHandler FromJavaScriptMessage;
/// <summary>
/// 提供給 JavaScript 呼叫的方法
/// </summary>
public void onOpenNativeShareDialog(string json)
{
FromJavaScriptMessage?.Invoke(null, json);
}
}
3. 在 WebView 的 NavigationStarting 事件發生時為每個新進入的網頁加入 JavaScriptExternalObject; 利用 AddWebAllowedObject 將 native Windows Runtime Component 宣告的類別加入到 Webview 之中,如下:
public sealed partial class MainPage : Page
{
private JavaScriptExternalObject javascriptExternalObject;
public MainPage()
{
javascriptExternalObject = new JavaScriptExternalObject();
javascriptExternalObject.FromJavaScriptMessage += JavascriptExternalObject_FromJavaScriptMessage;
this.InitializeComponent();
}
private void WebView_NavigationStarting(WebView sender, WebViewNavigationStartingEventArgs args)
{
// 定義公開的名稱為: external
// JavaScriptExternalObject: 為 Javascript 裏面可以利用 external.{JavaScriptExternalObject 裏面的内容}
sender.AddWebAllowedObject("external", javascriptExternalObject);
}
private async void JavascriptExternalObject_FromJavaScriptMessage(object sender, string e)
{
// 接受來自 JavasScript 的訊息
await this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () =>
{
var dialog = new MessageDialog(e);
await dialog.ShowAsync();
});
}
}
[注意]
如果註冊 external 的話,WebView 會自動覆寫原本 external 的 methods,所以 window.external.notify 也就無法使用了 (ScriptyNotify event 不會被觸發)。
需要自行在加入 notify method 到自定義的 JavaScriptExternalObject class 裏面才能使用哦。
4. WebView 載入 html 内容,並且測試内容;
html 内文如下:
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title>JavaScriptInvokeNativeSample</title>
<script type='text/javascript'>
function onInvokeNativeMethod() {
// invoke native method
external.onOpenNativeShareDialog("from javascript");
}
</script>
</head>
<body>
<button type="button" onclick="onInvokeNativeMethod();">Invoke native method</button>
</body>
</html>
XAML 内文如下:
<WebView HorizontalAlignment="Stretch" VerticalAlignment="Stretch" NavigationStarting="WebView_NavigationStarting"
Source="ms-appx-web:///html/TestPage.html" />
執行結果:
詳細的範例可參考 How to interoperate JS with native in WebView of Universal Windows Platform(UWP)。
[補充]
- 如果是自己開發或是受信任的網站,可以在 Package.appxmanifest 加入 ApplicationContentUriRules 的宣告,讓 Javasscript 可與 Windows RuntimeAPI 互動;
- 在 Win10 支援 WebView.Settings 可以設定 JavaScript 與 IndexedDB 是否啓用;
- WebView 支援 CaptureSelectedContentToDataPackageAsync 把 WebView 内容分享給其他 app,變成 DataPackage。因爲是非同步的 method 要記得處理 deferral 去保護 DataRequested 事件能執行完畢;
- 可利用 CapturePreviewToStreamAsync 將現在 WebView 的内容變成一張圖片;
- 預設 WebView 在 Desktop device family 是運作在 UI Thread,而其他 device family 則不是;可以設定 WebView.DefaultExecutionMode 讓預設的 UI thread 負責控制 WebView;
======
簡單筆記一下有這樣的用法,希望對大家在撰寫與網頁互動時也可以加入這樣的彈性。
References:
- WebView Class
- How to interoperate JS with native in WebView of Universal Windows Platform(UWP)
- Using JavaScript frameworks from your C#/UWP application
- App Lifecycle - Keep Apps Alive with Background Tasks and Extended Execution
- Create and consume an app service
- Create and register an out-of-process background task
- Hacking UWP WebView Part2