VisualBasic.NET2003のWebBrowser(InternetExplorerの機能を使ってHTMLを表示するコントロール)のメモです。
→VB6(ExcelVBA)のWebBrowserコントロール
→VC++2005のWebBrowserコントロール
VB.NET2003では、デフォルトではウェブブラウザーコントロール(WebBrowser)を使えない。使えるようにする為には、プロジェクトに設定してやる必要がある。
これにより ツールボックスの「全般」に「Microsoft Web Browser」が追加されるので、それをフォームに貼り付ける。
貼り付けると、
ソースには 以下のような変数が定義される。
Friend WithEvents AxWebBrowser1 As AxSHDocVw.AxWebBrowser
また、表示しているHTMLを操作するのにmshtmlという名前空間にあるHTMLDocumentクラスやElementクラスを使うことがあるが、それもデフォルトでは使えない。
ウェブページを表示する例。
Private Sub 開始() AddHandler AxWebBrowser1.DocumentComplete, AddressOf DocumentCompleteHandler AxWebBrowser1.Navigate("http://www.ne.jp/asahi/hishidama/home/tech/index.html") 'Navigateは、HTMLの読み込みを開始するだけ。 '読み込みが終了していなくても次の処理が実行される。 End Sub
'HTMLの読込が完了したときに呼ばれるイベントハンドラーを自分で定義する Private Sub DocumentCompleteHandler(ByVal sender As Object, ByVal e As AxSHDocVw.DWebBrowserEvents2_DocumentCompleteEvent) Debug.WriteLine("HTMLドキュメントの読み込み完了!") End Sub
ブラウザーの変数のDocumentCompleteイベントに自作のイベントハンドラーを登録してやると、ドキュメント(HTML)の読込が完了したとき
(かつ画面表示される前)に
そのハンドラーが呼ばれる。
このハンドラーが呼び出されたら、その処理が終わるまで 画面表示されない。
ブラウザーの中でリンクをクリックして別ページに移動した場合も、そのページの読込が完了したときに再びこのハンドラーが呼ばれる。
※読み込みの完了を待つには、BusyやReadyStateを使う方法もある。ExcelのVBAのページを参照。たぶん同じだから。
上記のソースではイベントハンドラーの登録にAddHanlderを使っているが、
使わない方法もある。[2007-03-16]
イベントを発生させたいオブジェクト(の変数宣言)にWithEventsを付けて(AxWebBrowser1では自動的に付いている)、ハンドラーの関数(サブルーチン)にHandlesを追加すればよい。
'HTMLの読込が完了したときに呼ばれるイベントハンドラーを自分で定義する Private Sub DocumentCompleteHandler(ByVal sender As Object, ByVal e As AxSHDocVw.DWebBrowserEvents2_DocumentCompleteEvent) _ Handles AxWebBrowser1.DocumentComplete 〜 End Sub
Dim オブジェクト As クラス
AddHandler オブジェクト.イベント名, AddressOf
ハンドラー関数
Function ハンドラー関数() As 型
↓↑
Dim WithEvents オブジェクト As クラス
Function ハンドラー関数() As 型 Handles
オブジェクト.イベント名
ちなみに、Handlesの後ろにイベントをカンマ区切りで並べていくことにより、1つのハンドラーを複数のイベントで呼び出すようにできるらしい。
HTML内部の操作(フォームの値の取得や設定、ボタンのクリック等)を行うには、まずWebBrowserからHTMLDocumentを取得する必要がある。
HTMLDocumentは、ドキュメントの読み込みが完了していないと使用できない。なので、HTML読込のハンドラーの中で取得しておくのがよいと思われる。
Dim doc As mshtml.HTMLDocument
'HTMLの読込が完了したときに呼ばれるイベントハンドラーを自分で定義する Private Sub DocumentCompleteHandler(ByVal sender As Object, ByVal e As AxSHDocVw.DWebBrowserEvents2_DocumentCompleteEvent) '以前のオブジェクトを解放 If Not doc Is Nothing Then System.Runtime.InteropServices.Marshal.ReleaseComObject(doc) End If doc = AxWebBrowser1.Document End Sub
※読み込んだHTMLがフレームを使用していた場合は、このイベントは複数発生する。
HTML内のフォームのINPUTタグの値を取得・設定できる。
アクセスしたいINPUTタグにidを付けておき、VBからはそのElementを取得し、それに対して操作する。
<input id="id1" name="name1" type="text" value="zzz">
Private Function 値取得() As String Dim ret As String Dim input1 As mshtml.HTMLInputElement input1 = doc.getElementById("id1") If Not input1 Is Nothing Then ret = input1.value '値の取得 input1.value = "abc" '値の設定 System.Runtime.InteropServices.Marshal.ReleaseComObject(input1) End If Return ret End Function
名前(name)で取得することも出来る。[2007-03-16]
この場合、複数のタグで同じ名前が付いている可能性があるので、複数の要素が返る。
Dim name1s As mshtml.IHTMLElementCollection name1s = d.getElementsByName("name1") For i As Integer = 0 To name1s.length - 1 Dim n1 As mshtml.HTMLInputElement = name1s.item(i) Debug.WriteLine(n1.value) End If System.Runtime.InteropServices.Marshal.ReleaseComObject(name1s)
HTMLタグ | クラス |
---|---|
input | mshtml.HTMLInputElement |
body | mshtml.HTMLBodyClass |
iframe | mshtml.HTMLIFrameClass |
div | mshtml.HTMLDivElementClass |
HTMLのdivやbody・iframe等のタグではスクロールすることができるが、VBからもスクロールさせることが出来る。[2007-03-21]
(要するにJavaScriptでスクロールさせるのと同じ)
Dim scr1 As mshtml.HTMLDivElementClass scr1 = doc.getElementById("scroll_div") If Not scr1 Is Nothing Then 'Debug.WriteLine(scr1.scrollLeft) 'Debug.WriteLine(scr1.scrollTop) scr1.scrollTop = scr1.scrollHeight / 2 '指定位置へ移動 System.Runtime.InteropServices.Marshal.ReleaseComObject(scr1) End If
HTML上のボタンをクリックするのも簡単に出来る。
<input id="btn1" type="button" value="ボタン1" onclick="confirm('ボタン1が押された')">
Private Sub ボタンをクリックする() Dim button1 As mshtml.HTMLInputElement button1 = doc.getElementById("btn1") If Not button1 Is Nothing Then button1.click() 'ボタンをクリックする System.Runtime.InteropServices.Marshal.ReleaseComObject(button1) End If End Sub
HTMLのボタンがクリックされたことをVB側で検知することも出来る。
が、ちょっと技巧を要する。
基本的には 取得したエレメントにイベントハンドラーを登録してやることにより、HTMLでイベントが発生するとそのハンドラーが呼ばれる。
のだが、なぜかエレメントのデフォルト動作が行われなくなる。
例えばボタンをクリックするとき、「マウスを押すとボタンが沈み、離すと元に戻る」のが通常の動作だが、ボタンが沈まなくなる!
これを回避する為に、隠れたINPUTタグを利用することにした。
本来対象としたいボタンのonclick()で、隠れたINPUTタグをクリックしてやる。
そして、イベントハンドラーは隠れたINPUTタグに対して登録しておく。
こうしておけば、ボタンの動作は通常通りで、(隠れたINPUTタグ経由で)VB側にイベントが通知される。
<input type="button" value="VBを呼び出すボタン" onclick="hidden_button.click()"> <!--隠れたタグをクリックする--> <input id="hidden_button" <!--こいつに対してクリックイベントを登録しておくと--> type="hidden"> <!--クリック時にVBのハンドラーが呼ばれる-->
Dim button2 As mshtml.HTMLInputElement
'HTMLの読込が完了したときに呼ばれるイベントハンドラーを自分で定義する Private Sub DocumentCompleteHandler(ByVal sender As Object, ByVal e As AxSHDocVw.DWebBrowserEvents2_DocumentCompleteEvent) 〜 '以前のオブジェクトを解放 If Not button2 Is Nothing Then System.Runtime.InteropServices.Marshal.ReleaseComObject(button2) End If button2 = doc.getElementById("hidden_button") If Not button2 Is Nothing Then AddHandler button2.onclick, AddressOf Button2ClickHandler End If End Sub
'HTMLのボタンがクリックされたときに呼ばれるハンドラー
Private Function Button2ClickHandler() As Boolean
MsgBox("HTMLのボタンがクリックされた", , "VBのダイアログ")
Return True '何を返しても動作が変わらない気がする…
End Function
なお、ボタンを【マウスでクリック】した時だけでなく、【フォーカスが当たっているときのEnterキー押下】でも ちゃんとクリックイベントが発生する。
WebBrowser内で別ページへ遷移する直前に、BeforeNavigate2というイベントが発生する。[2007-03-16]
(このイベント名でWebを検索すると「BeforeNavigate2のバグで
イベントを捕捉できない」という情報を見かけることがあるが、自分が使ったVB.NET2003(.NET Framework 1.1.4322 SP1)では大丈夫だった)
Private Sub BeforeNavigate(ByVal sender As Object, ByVal e As AxSHDocVw.DWebBrowserEvents2_BeforeNavigate2Event) _ Handles AxWebBrowser1.BeforeNavigate2 〜 End Sub
値 | 内容 | 備考 | 更新日 |
---|---|---|---|
sender | イベントが発生したWebBrowserのオブジェクト | すなわちAxWebBrowser1と同じだが、このイベントに結び付けられているWebBrowserコントロールが複数あるときに区別できる。 | 2007-03-30 |
e.uRL | 遷移先のURL | ||
e.headers | |||
e.targetFrameName | フレーム名 | HTMLのframeタグのname属性の値。frameタグでない場合は空文字列。 | 2007-03-30 |
e.postData | メソッドがPOSTの場合に送信されるデータ | バイト配列。ここに入っている値を書き換えても、実際に送信されるデータは変更されない。 | |
e.cancel | キャンセルするかどうか | ここにTrueを入れてハンドラー処理を終了すると、遷移がキャンセルされる。 |
HTMLのframeタグを使ってフレーム分割されているドキュメントをWebBrowserで読み込むときは、BeforeNavigateイベントやDocumentCompleteイベントが複数回 発生する。[2007-03-30]
<html> <head><title>フレームのイベントのテスト</title></head> <frameset rows="*,*"> <frame name="f1" src="test1.html"> <frame name="f2" src="test2.html"> </frameset> </html>
このfrm.htmlを読み込んだとき、イベントは以下のように発生する。
注意:子フレームのイベントが発生する順序は、たぶん不定。親フレーム(frm.html)のイベントは、たぶん常に最初と最後に発生する。
いずれもイベントの引数のsenderはAxWebBrowser1なので、senderでは区別がつかない。
DocumentCompleteイベントで親フレームが完了した(つまり全ドキュメントを読み込んだ)事を確認するには、以下のようにする。
Private Sub DocumentCompleteHandler(ByVal sender As Object, ByVal e As AxSHDocVw.DWebBrowserEvents2_DocumentCompleteEvent) _ Handles AxWebBrowser1.DocumentComplete If e.pDisp Is sender.Application Then '全て読み込み完了 End If 〜 End Sub
(pDispとWebBrowser1.Objectを比較するよう書いてあるHPもあるが、それはたぶんVB6.0の方法。
VB.NET2003ではAxWebBrowser1.Objectは存在しない。AxWebBrowser1.Applicationでいいと思う)
※子フレームの中のHTMLだけが遷移した場合は、そのフレームのイベントしか発生しない。
DocumentCompleteイベントでフレーム名を取得するには、以下のようにする。
Private Sub DocumentCompleteHandler(ByVal sender As Object, ByVal e As AxSHDocVw.DWebBrowserEvents2_DocumentCompleteEvent) _ Handles AxWebBrowser1.DocumentComplete Dim doc As mshtml.HTMLDocument = e.pDisp.Document Debug.WriteLine(doc.parentWindow.name) 'フレーム名 〜 End Sub
e.pDispはAxWebBrowserクラスではないようだが、e.pDisp.DocumentはHTMLDocumentのオブジェクトのようだ。
フレーム付きのドキュメントから特定のIDのHTMLエレメントを探すには、全フレームを再帰的に探索するしかないようだ。
'フレームの中から再帰的にIDのエレメントを探す(最初に見つかったエレメントを1つだけ返す) Private Function getElementById(ByRef doc As mshtml.HTMLDocument, ByRef name As String) As mshtml.IHTMLElement Dim e As Object = doc.getElementById(name) If Not e Is Nothing Then Return e End If Dim c As mshtml.FramesCollection = doc.frames '全フレームを列挙 For i As Integer = 0 To c.length - 1 Dim f As mshtml.HTMLWindow2 = c.item(i) e = getElementById(f.document, name) If Not e Is Nothing Then Return e End If Next End Function