User Tools

Site Tools


study:cs-webbrowser:20250603-001:index

C# 使用 WebView2 元件 (2025-06-03)

Local Backup

前言

  • 若要用 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# 函式

  • 原理:
    • 設計一個可以對 Javascript 公開的類別。
    • 向 Javascript 提供此類別。
    • Javascript 呼叫該類別提供的函式。
  • 設計一個可以對 Javascript 公開的類別
    • 底下設計一個類別,提供二個函式。
      • 一個是由主程式秀出由 JS 傳入的訊息。
      • 一個是由主程式計算出結果後,傳回 JS。
  • 類別前面要有這二行,才會被 JS 看到
    [ClassInterface(ClassInterfaceType.AutoDual)]
    [ComVisible(true)]
  • Code
    / 用於向網頁註冊 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());
  • Code
    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 按鈕,送訊息給網頁
  • 網頁有收到訊息了。
  • 再按下網頁中的「送出網頁訊息」
  • 主機也順利收到訊息了。

study/cs-webbrowser/20250603-001/index.txt · Last modified: 2025/06/03 15:57 by jethro