WebViewコントロールでの操作
WebViewコントロールでWebを操作する方法
WebView2コントロールでの操作
WebView2コントロールを使ってWebページを表示したりする方法は、左メニューの「基本操作」を参照してください。
基本的にはWebBrowserコントロールと大差ありません。
ただしDOM操作は大きく異なります。
WebBrowserコントロールでは直接DOM操作できるメソッド・プロパティ(getElementByIdメソッドやdocumentプロパティ等)がありましたが、
WebViewコントロールにはありません。
WebViewコントロールでは、InvokeScriptAsyncメソッドとJavaScriptのeval関数を使用して、JavaScriptのコードを使ってWebページを操作します。
また、WebページのJavaScriptからアプリケーションに情報を渡すことができます。
JavaScriptの「window.external.notify」と、アプリケーション側ではScriptNotifyイベントを使用します。
JavaScriptの関数を実行する/DOMを操作する
WebViewでJavaScriptを実行する場合、InvokeScript/InvokeScriptAsyncメソッドとJavaScriptのeval関数を使用します。
Dim userIdText As String = "abcde" Dim functionString As String = String.Format("document.getElementById('userid').innerText = '{0}';", userIdText) WebView1.InvokeScript("eval", New String() {functionString})
String userIdText = "abcde"; String functionString = String.Format("document.getElementById('userid').innerText = '{0}';", userIdText); WebView1.InvokeScript("eval", New String[] { functionString });
InvokeScript/InvokeScriptAsyncメソッドの第一引数に"eval"、第二引数に実行したいJavaScriptのコードの文字列を渡します。
上記のようにエレメントに値を設定したりできます。
もしevalで実行しようとしているJavaScriptの構文に誤りがあった場合、「System.AggregateException」の例外が発生します。
例外の詳細を確認しても何が間違っているのかはわかりません。
そんなときはブラウザの開発者ツールを使ってJavaScriptが正常に動作するか確認しましょう。
例えばChromeのデベロッパーツールではConsoleでJavaScriptのコードを入力して実行できます。
下記はセレクターのJavaScriptを実行して、期待通りに結果が得られていることが確認できます。
また後述している「window.external.notifyを使う場合の注意」も参考にしてください。
WebViewで表示しているページの情報を取得する
JavaScriptでwindow.external.notifyを使用することで、アプリケーションにイベントを通知して情報を渡すことができます。
まず、Webページ内の情報を受け取れるように、アプリケーション起動時にWebViewコントロールのIsScriptNotifyAllowedプロパティにTrueを設定します。
'VB.NET WebView1.IsScriptNotifyAllowed = True
アプリケーション側でこれらのメッセージを受け取るためにScriptNotifyイベントを定義します。受け取った値はe.Valueで取得します。
'VB.NET Private Sub OnWebViewScriptNotify(sender As Object, e As Microsoft.Toolkit.Win32.UI.Controls.Interop.WinRT.WebViewControlScriptNotifyEventArgs) Handles WebView1.ScriptNotify MsgBox(e.Value) End Sub
WebViewのInvokeScriptメソッドと、JavaScriptのeval関数を使うのは上記「JavaScriptの関数を実行する」と一緒ですが、
実行するJavaScriptコードとしてwindow.external.notifyを使用します。
JavaScript内でwindow.external.notifyを実行すると、引数に指定した値がアプリケーション側にScriptNotifyイベントで渡されます。
'VB.NET WebView1.InvokeScript("eval", New String() {"window.external.notify(String(document.getElementById('userid').value));"})
Public Class Form1 Private _getDataString As String 'WebViewからイベントで取得した文字列 Private _getDataFlag As Boolean 'WebViewからイベントが発生したか Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load WebView1.IsScriptNotifyAllowed = True 'これがないと、window.external.notifyでコード実行結果するとScriptNotifyイベントが発生しない。 End Sub Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click _getDataFlag = False 'JavaScriptを実行 WebView1.InvokeScript("eval", New String() {"window.external.notify(String(document.getElementById('userid').value));"}) Application.DoEvents() 'イベントが発生しているかチェック If _getDataFlag = False Then Return End If '取得できた値を表示 MsgBox(_getDataString) End Sub Private Sub OnWebViewScriptNotify(sender As Object, e As Microsoft.Toolkit.Win32.UI.Controls.Interop.WinRT.WebViewControlScriptNotifyEventArgs) Handles WebView1.ScriptNotify _getDataString = e.Value _getDataFlag = True End Sub End Class
public partial class Form1 : Form { private string _getDataString; //WebViewからイベントで取得した文字列 private bool _getDataFlag; //WebViewからイベントが発生したか public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { webView1.IsScriptNotifyAllowed = true; //これがないと、window.external.notifyでコード実行結果するとScriptNotifyイベントが発生しない。 } //WebViewで表示しているページの情報を取得する private void Button1_Click(object sender, EventArgs e) { _getDataFlag = false; //JavaScriptを実行 webView1.InvokeScript("eval", new string[] {"window.external.notify(String(document.getElementById(\'userid\').value));"}); Application.DoEvents(); //イベントが発生しているかチェック if (_getDataFlag == false) return; //取得できた値を表示 MessageBox.Show(_getDataString); } private void OnWebViewScriptNotify(object sender, Microsoft.Toolkit.Win32.UI.Controls.Interop.WinRT.WebViewControlScriptNotifyEventArgs e) { _getDataString = e.Value; _getDataFlag = true; } }
JavaScriptで処理を行った結果を取得する
JavaScriptで処理を行った結果を「window.external.notify」で取得することができます。
JavaScriptのeval関数に渡すJavaScriptコードとして、
実行したい処理と、その処理結果を「window.external.notify」に渡すコードを組みます。
'VB.NET WebView1.InvokeScript("eval", New String() {"var foo = document.querySelectorAll('.g');var str = """";foo.forEach(function (elem) { str += elem.textContent + '\n';});window.external.notify(str);"})
要素の指定や操作の方法について
具体的な要素の指定や操作の方法については、左メニューの「HTML要素の指定」
「HTML要素の操作(DOM操作)」を参照してください。
window.external.notifyを使う場合の注意
'VB.NET WebView1.InvokeScript("eval", New String() {"window.external.notify(String(document.getElementById('userid').value));"})
正常に動作しない場合は、以下のポイントを確認してください。
「window.external.notify」の引数はString型になっているか
「window.external.notify」の引数はString型でないとOnWebViewScriptNotifyイベントに値が渡されません。
Stringにキャストして値がわたるようにしましょう。
ダブルクォーテーションとシングルクォーテーションを使い分けているか
VB.NETでプログラムをする場合、2連続するダブルクォーテーションは実態は1つダブルクォーテーションになります。
意図せずダブルクォーテーションの数が減る場合がありますので、
JavaScriptのコード部分はシングルクォーテーションを使って区別しましょう。
'以下はVisualStudioではエラーにならない。 'しかし2連続のダブルクォーテーションは1つとして扱われるので、JavaScriptとしてはエラーになる。 WebView1.InvokeScript("eval", New String() {"var str = "";window.external.notify(str);"}) 'シングルクォーテーションで記述しましょう。 WebView1.InvokeScript("eval", New String() {"var str = '';window.external.notify(str);"})
WebViewのページ表示待ち方法
WebBrowserコントロールはIsBusy/ReadyStateプロパティで読み込み完了を判定していましたが、
WebViewにはこれらプロパティはありませんので、JavaScriptのdocument.readyStateを使います。
WebViewのNavigateメソッド実行後、InvokeScriptメソッドからWebView内のreadyStateの値を取得して読み込みが完了しているかループでチェックします。
以下のサンプルコードは、上記「JavaScriptの関数を実行する」のサンプルコードで使っている変数や設定を使ってますので、一部流用してください。
Public Class Form1 Private _getDataString As String 'WebViewからイベントで取得した文字列 Private _getDataFlag As Boolean 'WebViewからイベントが発生したか Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load WebView1.IsScriptNotifyAllowed = True 'これがないと、window.external.notifyでコード実行結果するとScriptNotifyイベントが発生しない。 End Sub Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click Dim openFlag As Boolean = False '読み込み完了フラグ 'ページを表示する WebView1.Navigate("https://web.biz-prog.net/") '読み込み待ち Dim timeout As Date = Now.AddMinutes(30) While Now < timeout System.Windows.Forms.Application.DoEvents() System.Threading.Thread.Sleep(100) 'JavaScriptを実行 _getDataFlag = False WebView1.InvokeScript("eval", New String() {"window.external.notify(String(document.readyState == 'complete'));"}) Application.DoEvents() 'イベントが発生しているかチェック If _getDataFlag = False Then MsgBox("err") Return End If '読み込み完了してれば終了 If _getDataString = "true" Then openFlag = True Exit While End If End While If openFlag = True Then MsgBox("ok") Else MsgBox("timeout") End If Return End Sub Private Sub OnWebViewScriptNotify(sender As Object, e As Microsoft.Toolkit.Win32.UI.Controls.Interop.WinRT.WebViewControlScriptNotifyEventArgs) Handles WebView1.ScriptNotify _getDataString = e.Value _getDataFlag = True End Sub End Class
public partial class Form1 : Form { private string _getDataString; //WebViewからイベントで取得した文字列 private bool _getDataFlag; //WebViewからイベントが発生したか public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { webView1.IsScriptNotifyAllowed = true; //これがないと、window.external.notifyでコード実行結果するとScriptNotifyイベントが発生しない。 } //WebViewのページ表示待ち方法 private void button2_Click(object sender, EventArgs e) { bool openFlag = false; //読み込み完了フラグ webView1.Navigate("https://web.biz-prog.net/"); //読み込み待ち DateTime timeout = DateTime.Now.AddMinutes(30); while ((DateTime.Now < timeout)) { System.Windows.Forms.Application.DoEvents(); System.Threading.Thread.Sleep(100); //JavaScriptを実行 _getDataFlag = false; webView1.InvokeScript("eval", new string[] {"window.external.notify(String(document.readyState == 'complete'));"}); Application.DoEvents(); //イベントが発生しているかチェック if (_getDataFlag == false) { MessageBox.Show("err"); return; } //読み込み完了してれば終了 if ((_getDataString == "true")) { openFlag = true; break; } } if ((openFlag == true)) MessageBox.Show("ok"); else MessageBox.Show("timeout"); return; } private void OnWebViewScriptNotify(object sender, Microsoft.Toolkit.Win32.UI.Controls.Interop.WinRT.WebViewControlScriptNotifyEventArgs e) { _getDataString = e.Value; _getDataFlag = true; } }
WebViewのページ表示待ち方法2
ページの表示完了時に発生するイベントNavigationCompletedを使った表示待ちです。
Public Class Form1 Private ReadOnly condition As New System.Threading.CountdownEvent(1) Private Async Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click Dim result As String = "" '非同期実行 Await Task.Run( Sub() 'ページの表示を開始 WebView1.Navigate("https://web.biz-prog.net/") '読み込み完了まで待機 If condition.Wait(5000) Then result = "ok" Else result = "timeout" End If End Sub ) MsgBox(result) End Sub Private Sub WebView1_NavigationCompleted(sender As Object, e As Microsoft.Toolkit.Win32.UI.Controls.Interop.WinRT.WebViewControlNavigationCompletedEventArgs) Handles WebView1.NavigationCompleted Console.WriteLine(e.Uri.ToString()) '読み込み結果を判定 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
public partial class Form1 : Form { public Form1() { InitializeComponent(); } private readonly System.Threading.CountdownEvent condition = new System.Threading.CountdownEvent(1); private async void button3_Click(object sender, EventArgs e) { string result = ""; //非同期実行 await Task.Run(() => { //ページの表示を開始 webView1.Navigate("https://web.biz-prog.net/"); //読み込み完了まで待機 if (condition.Wait(5000)) result = "ok"; else result = "timeout"; }); MessageBox.Show(result); } private void webView1_NavigationCompleted(object sender, Microsoft.Toolkit.Win32.UI.Controls.Interop.WinRT.WebViewControlNavigationCompletedEventArgs e) { Console.WriteLine(e.Uri.ToString()); //読み込み結果を判定 if (e.IsSuccess) Console.WriteLine("complete"); else Console.WriteLine(e.WebErrorStatus); //シグナル初期化 condition.Signal(); System.Threading.Thread.Sleep(1); condition.Reset(); } }
参考