C# 使用 WebView2 元件

前言

 

若要用 C# 寫有瀏覽器界面的程式,過去是用 WebBrowser 元件,這是採用 IE 的核心。

 

在微軟放棄 IE 後,這個核心預計 2029 年也要消失了,目前微軟建議使用 WebView2 這個新元件,這是也 Edge (Chromium) 的核心,底下就是針對這個新元件做的一些測試。

 

使用者若要執行含有 WebView2 元件的程式,除了 Win11 及部份 Win10 已經內建之外,有些使用者是必須安裝 WebView Runtime,這部份在此就不多說了。

 

安裝 WebView2 元件

 

Visual Studio 中,預設是沒有 webView2 元件的。

 

在方案總管中的專案上按右鍵,選「管理 NuGet 套件」。

 

搜尋 webview2,找到 Microsoft.Web.WebView2 後,安裝它即可。

 

 

安裝後即可在工具箱中看到 WebView2 的元件,基本的使用和其它元件相同,拉到 Form 中就可以了。

 

 

WebView2 基本屬性

 

最基本的屬性,大概算是 Source,這是填入要瀏覽的網頁。

 

 

在程式中,也可以使用如下:

 

webView.Source = new Uri("https://...");

 

 

webView.CoreWebView2.Navigate("https://...");

 

注意,CoreWebView2 是更核心的物件,要使用之前,要先判斷是否已經存在,如果是自行創建的空元件,沒有瀏覽任何網頁,要先執行非同步的 EnsureCoreWebView2Async 才會觸發元件的 CoreWebView2 的初始化。

 

C# 程式執行 JavaScript 或新增初始化腳本

 

使用 webView.CoreWebView2.ExecuteScriptAsync("...")

可直接在網頁中執行寫入的 js 程式,或是利用它呼叫網頁中已有的 js 函式。

 

下面是官方範例,在檢查網址是 http:// 開頭之後,利用 js 的 alert 警告使用者 http:// 開頭的網址不安全。

 

using Microsoft.Web.WebView2.Core;

 

private void webView_NavigationStarting(object sender, CoreWebView2NavigationStartingEventArgs e)

{

    String uri = e.Uri;

    if (!uri.StartsWith("https://")) {

webView.CoreWebView2.ExecuteScriptAsync($"alert('{uri} is not safe, try an https link')");

        e.Cancel = true;

    }

}

 

也可以使用如下功能

 

webView.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync("...");

 

可以設定網頁載入完成就會執行的 js 程式。

 

底下是官方範例,是設計一段 message 的監聽程式。

 

webView.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync("window.chrome.webview.addEventListener(\'message\', event => alert(event.data));");

 

網頁執行 C# 函式

 

原理:

  1. 設計一個可以對 Javascript 公開的類別。
  2. 向 Javascript 提供此類別。
  3. Javascript 呼叫該類別提供的函式。

 

設計一個可以對 Javascript 公開的類別

 

底下設計一個類別,提供二個函式。

 

一個是由主程式秀出由 JS 傳入的訊息。

一個是由主程式計算出結果後,傳回 JS。

 

類別前面要有這二行,才會被 JS 看到

[ClassInterface(ClassInterfaceType.AutoDual)]

[ComVisible(true)]

 

 

// 用於向網頁註冊 C# 對象,供 JS 調用的類別

[ClassInterface(ClassInterfaceType.AutoDual)]

[ComVisible(true)]

 

public class WebViewClass

{

    // 秀出由 JS 傳入的訊息

    public void ShowMessage(string message)

    {

        MessageBox.Show(message);

    }

 

    // 計算出結果後,傳回 JS

    public int GetResult(int x, int y)

    {

        int r = x + y;

        // 多了底下的秀出功能時,傳回 js 就有問題了。

        // MessageBox.Show(r.ToString());

        return r;

    }

}

 

 

原本 GetResult 是想要秀出後再傳回,但有這行後

MessageBox.Show(r.ToString());

網頁就沒有印出結果,原因不明。 

 

向 Javascript 提供此類別

 

本例用底下的方法向網頁提供可呼叫的類別,最主要是這一行

 

webView.CoreWebView2.AddHostObjectToScript("webViewClass", new WebViewClass());

 

public Form1()

{

    InitializeComponent();

    // 處理網頁相關的資料

    InitializeAsync();

}

async void InitializeAsync()

{

    // 要先執行非同步的 EnsureCoreWebView2Async 才會觸發控件的 CoreWebView2 的初始化

    await webView.EnsureCoreWebView2Async(null);

    // 向網頁提供可呼叫的類別 WebViewClass,第一個參數是名稱,第二個參數是類別實體

    webView.CoreWebView2.AddHostObjectToScript("webViewClass", new WebViewClass());

}

 

Javascript 呼叫該類別提供的函式

 

測試網頁如下:

 

<body>

    <p>測試呼叫 WebView2 主機</p>

    <a href="" onclick="test1();">

        測試一:主機印出訊息 "Message From Web"。</a>

        <br>

    <a href="" onclick="test2();">

        測試二:印出主機傳回訊息。</a>

    <script>

        // 指定主機程式中可供 js 呼叫的類別

        var host = window.chrome.webview.hostObjects.webViewClass;

        async function test1()

        {

            // 呼叫主機程式中的 ShowMessage 函式

            await host.ShowMessage("Message From Web");

            return false;

        }

        // 有異步函式,要用 async

        async function test2()

        {

            // 呼叫主機程式中的 GetResult 函式,因為要等傳回值,所以要用 await 異步處理

            var ans = await host.GetResult(20,30);

            alert(ans);

            return false;

        }

    </script>

</body>

 

第一個測試 test1() 是呼叫主機的 webViewClass.ShowMessage ,傳入 "Message From Web",希望主機印出來。

 

第一個測試 test2() 是呼叫主機的 webViewClass.GetResult ,傳入 20 和 30 二個參數,希望主機加總後傳回來,再由 JS 印出來。

 

注意,因為這些是異步函式,所以呼叫要加上 await

 

await host.GetResult(20,30);

 

函式的前面也要加 async

 

test1() 因為不用等待結果,似乎有沒有指定異步都可以執行。

 

執行畫面


 

 

按下測試一,果然有印出 Message From Web。

 

 

按下測試二,也成功收到結果,並且由網頁印出來。

 

 

不過,偶有失敗沒有呈現結果的情況,原因不明。

 

如果 test2() 沒有使用異步呼叫,則會呈現如下。

 

 

主程式與 Web 溝通

 

主機和 Web 內容可用 postMessage 來彼此通訊,如下所示:

 

1. WebView2 控制項中的 Web 內容可用 window.chrome.webview.postMessage 來將訊息張貼到主機。

 

主機會使用主機上任何已註冊 WebMessageReceived 的來處理訊息

 

2. 主機使用 CoreWebView2.PostWebMessageAsString 或 CoreWebView2.PostWebMessageAsJSON ,將訊息張貼至 WebView2 控制項中的 Web 內容。

 

新增的處理常式會攔截這些 window.chrome.webview.addEventListener 訊息。

 

底下是做一個簡單的網頁,主程式載入網頁後,雙方互相送出訊息。

 

測試網頁 test.htm 

 

<html>

    <head>

        <title>Test Post Message</title>

    </head>

<body>

    <p>測試 Post Message</p>

    <a href="" onclick="window.chrome.webview.postMessage('Web Message'); return false;">送出網頁訊息</a>

    <script>

window.chrome.webview.addEventListener("message", e => {

            alert("網頁收到:" + e.data);

    });

    </script>

</body>

</html>

 

以上網頁中,送出訊息的是一個連結,點下文字會送出 "Web Message" 這個字串。

 

<a href="" onclick="window.chrome.webview.postMessage('Web Message'); return false;">送出網頁訊息</a>

 

接收訊息是一個監聽 message,收到訊息會印出 "網頁收到:XXXX"

 

主程式:

 

private void button1_Click(object sender, EventArgs e)

{

    if (webView.CoreWebView2 != null) {

webView.CoreWebView2.PostWebMessageAsString("webView2 Message");

    }

}

 

private void webView_WebMessageReceived(object sender, CoreWebView2WebMessageReceivedEventArgs e)

{

    string msg = e.TryGetWebMessageAsString();

    MessageBox.Show($"主機收到:{msg}");

}

 

以上主程式中,送出訊息的是一個 button1 的按鈕,按下按鈕會送出 "webView2 Message" 這個字串。

 

webView.CoreWebView2.PostWebMessageAsString("webView2 Message");

 

接收訊息是一個 webView 的事件 WebMessageReceived,這是可以直接在 WebView2 元件設定,收到訊息會印出 "主機收到:XXXX"

 

 

實際執行

 

 

按下上方 Send Msg 按鈕,送訊息給網頁

 

 

網頁有收到訊息了。

 

再按下網頁中的「送出網頁訊息」

 

主機也順利收到訊息了。

 
重要度:
文章分類:

發表新回應

借我放一下廣告