WebBrowserからWebView2への切り替え

WebBrowserからWebView2への切り替え方法

以前はC#/VB.NETで使用するWebブラウザコントロールはWebBrowserが使われていましたが、
IE11ベースのWebBrowserではサイトが表示できなくなってきていますので、
新しいChromiumベースのWebView2コントロールに切り替えをおすすめします。

WebBrowserとWebView2ではプログラムの方法が色々と異なりますので、
ここではWebBrowserからWebView2に切り替える際のポイントについて解説します。

フォーム画面へのコントロール配置について

WebBrowserもWebView2もVisualStudioのデザイナでフォーム画面に張り付ければよいのですが、
WebView2はデフォルトではツールボックスに存在しないので、追加する必要があります。

WebView2ランタイムのインストール

ここから WebView2ランタイムの「Evergreen Bootstrapper」をインストールしましょう。
Windows11の場合は最初からWebView2ランタイムが入っていますので、インストールは不要です。
WebView2ランタイムの「Evergreen Bootstrapper」をインストール

WebView2 SDKのインストール

NuGetから「WebView2」のSDKをインストールします。

メニューの[ツール]->[NuGetパッケージマネージャー]->[ソリューションのNuGetパッケージの管理]を選択します。
NuGetパッケージの管理

検索欄に「WebView2」を入力し、右側で適用するプロジェクトを選択してインストールします。
バージョンは最新の安定版のものを選びます。
Microsoft.Toolkit.Forms.UI.Controls.WebView

しばらく待てばツールボックスにWebView2が現れました。
表示されない場合はVisualStudioを再起動してください。
ツールボックス

WebView2の初期化処理

WebView2コントロールはプログラムで初期化が必要です。
WebView2を使用する前までにWebView2.EnsureCoreWebView2Asyncメソッドを実行してください。

							Private Sub Form_Load(sender As Object, e As EventArgs) Handles MyBase.Load

								InitializeAsync()
				
							End Sub
							
							Private Async Sub InitializeAsync()
				
								Await wv2.EnsureCoreWebView2Async(Nothing)
				
							End Sub
						
							public Form1()
							{
								InitializeComponent();
								InitializeAsync();
							}
				
							async void InitializeAsync()
							{
								await wv2.EnsureCoreWebView2Async(null);
							}
						

基本的なブラウザ操作について

基本的なブラウザの操作(ページを開く、進む、戻る、リロード、等)はWebBrowserと同じようなメソッドがあります。
単純に書き換えれば動作します。
以下が対応表です。

WebBrowser WebView2
ページを開く wb.Navigate("") wv2.CoreWebView2.Navigate("")
戻る wb.GoBack() wv2.GoBack()
進む wb.GoForward() wv2.GoForward()
読み込み中止 wb.Stop() wv2.Stop()
リロード wb.Refresh() wv2.Reload()
タイトルの取得 wb.Document.Title wv2.CoreWebView2.DocumentTitle
URLの取得 wb.Document.Url wv2.CoreWebView2.Source

ページの表示待ち方法

ページの読み込み完了を待機する処理として、WebBrowserではIsBusyReadyStateプロパティをループでチェックするのが 主流でしたが、WebView2にはこれらプロパティはありません。
以下のサンプルのようにNavigationCompletedの発生を待つ方法があります。

						Imports Microsoft.Web.WebView2.Core

						Public Class Form1

							Private ReadOnly condition As New System.Threading.CountdownEvent(1)

							Private Sub FormWebView2_Load(sender As Object, e As EventArgs) Handles MyBase.Load
								InitializeAsync()
							End Sub
						
							Async Sub InitializeAsync()
								Await WebView2.EnsureCoreWebView2Async(Nothing)
							End Sub

							Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
						
								Dim result As String = ""
								'サイトの表示を開始
								WebView2.CoreWebView2.Navigate("https://web.biz-prog.net/")						

								'非同期実行
								Await Task.Run(
									Sub()
										'読み込み完了まで待機
										If condition.Wait(5000) Then
											result = "ok"
										Else
											result = "timeout"
										End If
									End Sub
								)
						
								MsgBox(result)
						
							End Sub
						
							Private Sub WebView2_NavigationCompleted(sender As Object, e As CoreWebView2NavigationCompletedEventArgs) Handles WebView2.NavigationCompleted
						
								'読み込み結果を判定
								If e.IsSuccess Then
									Console.WriteLine("complete")
								Else
									Console.WriteLine(e.WebErrorStatus)
								End If
						
								'シグナル初期化
								condition.Signal()
								System.Threading.Thread.Sleep(1)
								condition.Reset()
						
							End Sub
					
						End Class
					
						using System;
						using System.Text;
						using System.Threading;
						using System.Threading.Tasks;
						using System.Windows.Forms;
						using Microsoft.Web.WebView2.Core;

						public partial class Form1 : Form
						{
							readonly CountdownEvent condition = new CountdownEvent(1);

							private void Form1_Load(object sender, EventArgs e)
							{
								InitializeAsync();
							}
					
							async void InitializeAsync()
							{
								await webView2.EnsureCoreWebView2Async(null);
								webView2.CoreWebView2.NavigationCompleted += webView2_NavigationCompleted;
							}

							private async void button1_Click(object sender, EventArgs e)
							{
								string result = "";
								
								//サイトの表示を開始
								webView2.CoreWebView2.Navigate("https://web.biz-prog.net/");
					
								//非同期実行
								await Task.Run(() =>
								{
									//読み込み完了まで待機
									if (condition.Wait(5000))
										result = "ok";
									else
										result = "timeout";
								});
					
								MessageBox.Show(result);
							}
					
							private void webView2_NavigationCompleted(object sender, CoreWebView2NavigationCompletedEventArgs e)
							{
								//読み込み結果を判定
								if (e.IsSuccess)
									Console.WriteLine("complete");
								else
									Console.WriteLine(e.WebErrorStatus);
					
								//シグナル初期化
								condition.Signal();
								System.Threading.Thread.Sleep(1);
								condition.Reset();
							}
						}	
					

DOMの操作方法について

WebBrowserコントロールではDOM操作が行えるメソッド/プロパティがありましたが、
WebView2コントロールにはこのようなメソッドはありません。

WebView2コントロールでは、ExecuteScriptAsyncメソッドを使用して、JavaScriptのコードを実行してWebページを操作します。

ExecuteScriptAsyncメソッドの引数に、実行したいJavaScriptのコードの文字列を渡して実行します。
JavaScriptのコードで、DOM要素にテキストを設定したり、フォームをSubmitします。

また、DOM等の値を取得する場合は、JavaScriptで値を参照するコードを書けば、ExecuteScriptAsyncメソッドの戻り値として取得できます。

							Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click

								'Yahooの検索欄に文字を設定する
								Await WebView2.ExecuteScriptAsync($"document.getElementsByName('p')[0].value = 'プログラムでネットサーフィン!';")
						
								'検索欄の文字列を取得する
								Dim result = Await WebView2.ExecuteScriptAsync("document.getElementsByName('p')[0].value")
								MessageBox.Show(result)
						
								'submitして検索を実行
								Await WebView2.ExecuteScriptAsync($"document.forms['sf1'].submit();")
						
							End Sub
						
							async private void button1_Click(object sender, EventArgs e)
							{
								//Yahooの検索欄に文字を設定する
								await webView2.ExecuteScriptAsync($"document.getElementsByName('p')[0].value = 'プログラムでネットサーフィン!';");
					
								//検索欄の文字列を取得する
								var result = await webView2.ExecuteScriptAsync("document.getElementsByName('p')[0].value");
								MessageBox.Show(result);
					
								//submitして検索を実行
								await webView2.ExecuteScriptAsync($"document.forms['sf1'].submit();");
							}
						

上記、スクリプトを複数に分けて実行していますが、1つにまとめて実行することもできます。
							Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click

								'Yahooの検索欄に文字を設定する。submitして検索を実行
								Await WebView2.ExecuteScriptAsync($"document.getElementsByName('p')[0].value = 'プログラムでネットサーフィン!';
																	document.forms['sf1'].submit();")
						
							End Sub
						
							async private void button1_Click(object sender, EventArgs e)
							{
								//Yahooの検索欄に文字を設定する。submitして検索を実行
								await webView2.ExecuteScriptAsync($"document.getElementsByName('p')[0].value = 'プログラムでネットサーフィン!';
																	document.forms['sf1'].submit();");
							}
						

関数を定義して長いコードを記述し、jsonで実行結果を取得するようにしてみました。
							Dim js As String = 
								"var LinkClick_MatchText = function (text) {
									for (let i = 0; i < document.links.length; i++) {
										if (document.links[i].innerText == text) {
											document.links[i].click();
											return { ""result"": ""ok"" }
										}
									}
									return { ""result"" : ""notarget""}
								}
								LinkClick_MatchText('テキスト')"
							Dim jsonString As String = Await wb.ExecuteScriptAsync(js)
							Dim json = JsonSerializer.Deserialize(Of Dictionary(Of String, String))(jsonString)
							Console.WriteLine(json("result"))
						
							var js =
								@"var LinkClick_MatchText = function (text) {
									for (let i = 0; i < document.links.length; i++)
									{
										if (document.links[i].innerText == text)
										{
											document.links[i].click();
											return { ""result"": ""ok"" }
										}
									}
									return { ""result"" : ""notarget""}
								}
								LinkClick_MatchText('テキスト')";
							var jsonString = await wb.ExecuteScriptAsync(js);
							var json = JsonSerializer.Deserialize<Dictionary<String, String>>(jsonString);
							Console.WriteLine(json["result"]);
						

実行するJavaScriptの中で、async/awaitの非同期で実行するコードを書いてみましたが、実行するとそこで処理が中断されました。
sleepさせようとループをいれても描画自体がとまってうまくいきません。
もしかしたら実行するJavaScriptは非同期処理には対応していないのかもしれません。

要素の指定や操作方法のサンプルについては、左メニューの「WebView2コントロール HTML要素の指定」 「WebView2コントロール HTML要素の操作(DOM操作)」を参照してください。