2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

C#でのWebView2の使い方メモ

Posted at

1.WebView2の初期化を行う。

1.NuGetでMicrosoft.Web.WebView2をインストールします。
2.Visual StudioのツールボックスのWebView2をGUIデザイナでFormに配置します。
3.FormのOnLoad内でEnsureCoreWebView2Asyncを実行しWebView2を初期化します。
 ※awaitで確実に完了を待ちます。

namespace TestApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        protected override async void OnLoad(EventArgs e)
        {
            var environment = await CoreWebView2Environment.CreateAsync(null, System.IO.Directory.GetCurrentDirectory());
            await this.WebView2.EnsureCoreWebView2Async(environment);
            base.OnLoad(e);
        }
    }
}

実行すると、about:blankページが表示されます。
1.jpg


2.WebView2に表示するWebページを作る。

次に、WebView2で表示するhtmlファイルを作ります。
index.htmlというファイル名にしてDドライブ直下に保存しました。
JavaScriptは後で追加します。

<!DOCTYPE HTML>
<html>
<head>
    <meta charset="UTF-8" />
</head>
<body>
テスト
</body>
</html>

3.WebView2でWebページを表示する。

WebView2でindex.htmlを表示する処理を追加します。

CoreWebView2InitializationCompletedイベントでWebView2コントロールの初期化の完了を捕捉してから表示(Navigateを実行)する必要があります。

using Microsoft.Web.WebView2.Core;
using System;
using System.Windows.Forms;

namespace TestApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            this.WebView2.CoreWebView2InitializationCompleted += WebView2_CoreWebView2InitializationCompleted;
        }

        protected override async void OnLoad(EventArgs e)
        {
            var environment = await CoreWebView2Environment.CreateAsync(null, System.IO.Directory.GetCurrentDirectory());
            await this.WebView2.EnsureCoreWebView2Async(environment);
            base.OnLoad(e);
        }

        private void WebView2_CoreWebView2InitializationCompleted(object sender, CoreWebView2InitializationCompletedEventArgs e)
        {
            if (e.IsSuccess)
            {
                this.WebView2.CoreWebView2.Navigate(@"D:\index.html");
            }
        }
    }
}

実行すると、下記のように表示されます。
2.jpg

4.コントローラークラスを作る。

WebView2とのデータ送受信を行うコントローラークラスを作ります。
MVCモデルのコントローラーに該当すると思われる為、クラス名はControllerにしました。

Controllerは、WebView2への参照を保持し、その参照を通じて送受信を行います。
クラスに[ComVisible(true)]属性を付け、publicにする必要があります。

namespace TestApp1
{
    [ComVisible(true)]
    public class Controller
    {
        private WebView2 View { get; set; }
        
        public Controller(WebView2 view)
        {
            View = view;
        }
    }
}

5. C#からJavaScriptへのデータ送信

C#からJavaScriptへデータを送信出来るようにします。
コントローラーに3つのメソッドを作ります。

メソッド名 機能 JSでの受け取り方
1 PostWebMessageAsString 文字列送信 messageイベント
2 PostWebMessageAsJson JSON文字列送信 messageイベント
必要に応じてJSON.parseする
3 ExecuteScriptAsync JSの関数を実行 呼び出されるので受け取り手順なし
JSからC#への戻り値は常にJSON
※参照
namespace TestApp1
{
    [ComVisible(true)]
    public class Controller
    {
        private WebView2 View { get; set; }
        public Controller(WebView2 view)
        {
            View = view;
        }

        public void PostWebMessageAsString(string webMessageAsString)
        {
            this.View.CoreWebView2.PostWebMessageAsString(webMessageAsString);
        }

        public void PostWebMessageAsJson(string webMessageAsJson)
        {
            this.View.CoreWebView2.PostWebMessageAsJson(webMessageAsJson);
        }

        public async Task<string> ExecuteScriptAsync(string script)
        {
           //フォームから呼び出されるコントローラーなので、同期コンテキストに戻らないようにする。
           return await this.View.ExecuteScriptAsync(script).ConfigureAwait(false);
        }
    }
}

WebView2をホストしているフォームに、Controllerを追加します。
また、EnsureCoreWebView2Asyncより後にNavigationCompletedイベントを登録します。

namespace TestApp1
{
    public partial class Form1 : Form
    {
        private Controller Controller;
    
        public Form1()
        {
            InitializeComponent();
            this.WebView2.CoreWebView2InitializationCompleted += WebView2_CoreWebView2InitializationCompleted;
        }

        protected override async void OnLoad(EventArgs e)
        {
            var environment = await CoreWebView2Environment.CreateAsync(null, System.IO.Directory.GetCurrentDirectory());
            await this.WebView2.EnsureCoreWebView2Async(environment);
            this.WebView2.NavigationCompleted += WebView2_NavigationCompleted;
            base.OnLoad(e);
        }

        private void WebView2_CoreWebView2InitializationCompleted(object sender, CoreWebView2InitializationCompletedEventArgs e)
        {
            if (e.IsSuccess)
            {
                //ControllerのViewプロパティに、Form1のWebView2を登録するのが目的。
                this.Controller = new Controller(this.WebView2);
                this.WebView2.CoreWebView2.Navigate(@"D:\index.html");
            }
        }
        
        private void WebView2_NavigationCompleted(object sender, CoreWebView2NavigationCompletedEventArgs e)
        {
        }
    }
}

前述した3つのメソッドを作成したので、実際に試してみます。

①PostWebMessageAsStringで文字列を送信

ここではテストだけの為に、NavigationCompletedの発生時に送信しています。
殆どの場合は、Formのボタンクリック等のイベントで送信する事になると思います。

NavigationCompletedイベントの発生以降にPostMessageを実行する理由は、JavaScript等のコンテンツの読み込みの完了を待つ必要があるからです。
WebView2 アプリのナビゲーション イベント

private void WebView2_NavigationCompleted(object sender, CoreWebView2NavigationCompletedEventArgs e)
{
    if (e.IsSuccess)
    {
        this.Controller.PostWebMessageAsString("★テストメッセージ★");
    }
}

index.html側では、JavaScriptのmessageイベントで受け取ります。

<body>
    <meter id="meter" min="0" max="100" value="0"></meter>
    <br />
    <select id="language_select"></select>

    <script>
        window.chrome.webview.addEventListener('message', event => {
            alert("受信したメッセージ: " + event.data);
        });
    </script>
</body>

実行すると、下記のalertが表示されます。
3.jpg

②PostWebMessageAsJsonでJSON文字列を送信

C#(Form1)の送信処理は下記です。

private void WebView2_NavigationCompleted(object sender, CoreWebView2NavigationCompletedEventArgs e)
{
    if (e.IsSuccess)
    {
        var lang = new{ Name = "C#", FirstRelease = 2002 };
        this.Controller.PostWebMessageAsJson(JsonSerializer.Serialize(lang));
    }
}

JavaScript側では、stringifyしてからparseするとJSONオブジェクトとして取り扱えます。

<script>
    window.chrome.webview.addEventListener('message', event => {
        const jsonString = JSON.stringify(event.data);
        const obj = JSON.parse(jsonString);
        alert(obj.Name);
    });
</script>

実行すると、下記のalertが表示されます。
4.jpg


③ExecuteScriptAsyncでJavaScriptの関数を実行する。
 (ついでに戻り値も取得する。)

JavaScriptのmyFunc関数を呼び出しています。
戻り値が必要な場合、JavaScriptでreturnします。
ExecuteScriptAsyncの戻り値は常にJSON文字列なので、C#でDeserializeが必要です。

戻り値が文字列の場合

private async void WebView2_NavigationCompleted(object sender, CoreWebView2NavigationCompletedEventArgs e)
{
    if (e.IsSuccess)
    {
        string data = "あいうえお";
        //JavaScriptのmyFunc関数を直接呼び出している。
        //myFuncに文字列として渡したいので、myFuncの引数を''(シングルクォート)で囲んでいる。
        string str = await this.Controller.ExecuteScriptAsync($"myFunc('{data}')");
        //戻り値strの値はダブルクォーテーションがあるJSON「"あいうえおかきくけこ"」なので、
        //デシリアライズして、「あいうえおかきくけこ」にする。
        str = JsonSerializer.Deserialize<string>(str);
        MessageBox.Show(str);
    }
}
<script>
function myFunc(val) {
    return val + "かきくけこ";
}
</script>

7.jpg

戻り値が数値の場合

private async void WebView2_NavigationCompleted(object sender, CoreWebView2NavigationCompletedEventArgs e)
{
    if (e.IsSuccess)
    {
        int data = 10;
        //JavaScriptのmyFunc関数を直接呼び出している。
        //myFuncに数値として渡したいので、myFuncの引数を''(シングルクォート)で囲んでいない。
        //戻り値strの値は「100」(string型)
        string str = await this.Controller.ExecuteScriptAsync($"myFunc({data})");
        //数値として使用したければデシリアライズする。
        int i = JsonSerializer.Deserialize<int>(str);
        //MessageBoxで表示する為に文字列に戻している。
        MessageBox.Show(i.ToString());
    }
}
<script>
function myFunc(val) {
    return val*10;
}
</script>

7.jpg

ここまで、C#からWebViewへのメッセージの送信方法でした。


6.JavaScriptからC#へのデータ送信

今度はJavaScriptからメッセージを送信してC#で受信する処理です。
方法は3つあります。

JavaScriptからの送信方法 C#側の受信方法
1 window.chrome.webview.postMessage(文字列) WebMessageReceivedで受信
2 window.chrome.webview.postMessage(JSON) WebMessageReceivedで受信
必要ならデシリアライズする
3 JavaScriptでC#のメソッドを実行する。 不要

①JavaScriptのwindow.chrome.webview.postMessageで文字列を送信し、C#のWebMessageReceivedで受信する。

JSのpostMessageからのメッセージを受信する為に、WebMessageReceivedを登録します。
タイミングは、EnsureCoreWebView2Asyncの直後にしています。

namespace TestApp1
{
    public partial class Form1 : Form
    {
        private Controller Controller;

        public Form1()
        {
            InitializeComponent();
            this.WebView2.CoreWebView2InitializationCompleted += WebView2_CoreWebView2InitializationCompleted;
        }

        protected override async void OnLoad(EventArgs e)
        {
            var environment = await CoreWebView2Environment.CreateAsync(null, System.IO.Directory.GetCurrentDirectory());
            await this.WebView2.EnsureCoreWebView2Async(environment);

            this.WebView2.NavigationCompleted += WebView2_NavigationCompleted;
            this.WebView2.WebMessageReceived += WebView2_WebMessageReceived;
            base.OnLoad(e);
        }

        private void WebView2_CoreWebView2InitializationCompleted(object sender, CoreWebView2InitializationCompletedEventArgs e)
        {
            if (e.IsSuccess)
            {
                this.Controller = new Controller(this.WebView2);
                this.WebView2.CoreWebView2.Navigate(@"D:\index.html");
            }
        }

        private void WebView2_NavigationCompleted(object sender, CoreWebView2NavigationCompletedEventArgs e)
        {
        }

        private void WebView2_WebMessageReceived(object sender, CoreWebView2WebMessageReceivedEventArgs e)
        {
            MessageBox.Show(e.TryGetWebMessageAsString());
        }
    }
}
<script>
window.addEventListener("load", () => {
    window.chrome.webview.postMessage("JavaScriptからの送信テスト");
});
</script>

実行すると、C#のMessageBoxが表示されます。
5.jpg

②JavaScriptのwindow.chrome.webview.postMessageでJSON文字列を送信し、C#のWebMessageReceivedで受信する。

必要であれば、JsonSerializer.Deserialize でC#のオブジェクトに変換する。

private void WebView2_WebMessageReceived(object sender, CoreWebView2WebMessageReceivedEventArgs e)
{
    Lang lang = JsonSerializer.Deserialize<Lang>(e.WebMessageAsJson);
    MessageBox.Show(lang.Name);
}

class Lang
{
    public string Name { get; set; }
    public int FirstRelease { get; set; }
}
<script>
    window.addEventListener("load", () => {
        window.chrome.webview.postMessage({ "Name": "C#", "FirstRelease":2002});
    });
</script>

6.jpg

③JavaScriptからC#のメソッドを呼び出す。

まずは、JavaScriptから呼び出したいメソッドをControllerに追加する。

public class Controller
{
    public void TestMethod(string msg)
    {
        MessageBox.Show(msg);
    }
}

次に、WebView2.CoreWebView2.AddHostObjectToScript で、Controllerオブジェクトを、Controller1 という名前でWebView2に登録する。

public class Form1
{
    private Controller Controller;
    
    private void WebView2_CoreWebView2InitializationCompleted(object sender, CoreWebView2InitializationCompletedEventArgs e)
    {
        if (e.IsSuccess)
        {
            this.Controller = new Controller(this.WebView2);
            this.WebView2.CoreWebView2.AddHostObjectToScript("Controller1", this.Controller);
            this.WebView2.CoreWebView2.Navigate(@"D:\index.html");
        }
    }
}

JavaScript側で、chrome.webview.hostObjects を介して呼び出す。

<script>
    window.addEventListener("load", () => {
        chrome.webview.hostObjects.Controller1.TestMethod("てすと");
    });
</script>

結果、JavaScriptからC#のメソッドを呼び出せました。
6.jpg

役に立ちそうな資料

  1. WebView2 アプリのナビゲーション イベント
  2. ネイティブ側コードから Web 側コードを呼び出す
    ※ExecuteScriptAsyncの戻り値のstringがJSONであると明記されている。
  3. WebView2 アプリからの印刷
  4. コンテキストメニューのカスタマイズ
2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?