DOBON.NET DOBON.NETプログラミング掲示板過去ログ

VC#2005WebBrowserでFormを操作したい

環境/言語:[win2000pro .NET Framework2.0]
分類:[.NET]

以前VB6でWebBrowserコントロールを使ってHP上のFormをいろいろと操作したことがあります。
そのときは以下のようにやりました
'[VB6]
'Microsoft Internet Contorols、MSHTMLを参照設定
Dim obj_doc As HTMLDocument

WebBrowser1.Navigate("www.google.co.jp")

'ドキュメントの読み込み完了を待つ処理

obj_doc = WebBrowser1.document
obj_doc.getElementsByName("text")(0).Value = "a"
obj_doc.getElementsByName("select")(0).selectedIndex = 1
obj_doc.getElementsByName("submit")(0).click


これと同じことを、.NET Framework2.0から追加されたWebBrowserコントロール
を使って実現したいのですが、上記のようなやり方が使えません。
WebBrowserコントロールには何故かgetElementsByNameメソッドが実装されていません。
getElementByIDならあるのですが。
ドキュメントを漁ってみると、WebBrowserコントロールで非公開のメソッドについては
MSHTMLとWebBrowser1.Document.DomDocumentを組み合わせて使えるというような記述
があったのでいろいろと試行錯誤してみました。

//[C#]
//MSHTML.TLBを参照設定
object a; //適当なオブジェクトをでっち上げ
webBrowser1.Navigate("www.google.co.jp");

//ドキュメントの読み込みを待つ処理

mshtml.IHTMLDocument3 doc = (mshtml.IHTMLDocument3)wb.Document.DomDocument;
doc.getElementsByName("test").item(a,0).

とここまではよかったんですが、item(a,0).の.のあとにインテリセンスがValueを候補
として挙げてくれません。無理やりitem(a,0).Value = "a";などと指定すると
Object内にValueなんてないよと怒られます。

これとは別の以下のやり方で一応Textフィールドには値をセットできましたが、
checkboxやselectのやり方がわかりません。

mshtml.IHTMLDocument3 doc = (mshtml.IHTMLDocument3)wb.Document.DomDocument;
mshtml.IHTMLElementCollection elms = (mshtml.IHTMLElementCollection)doc.getElementsByName("q");
foreach (mshtml.IHTMLElement elm in elms)
{
elm.innerText = "a";
}

ここでも、例えばgetElementsByNameの引数をselectのものにしても、
elm.としたときにselectedIndexは候補にでません。

.NET Framework2.0のWebBrowserコントロールでFormの操作を実現した方おられましたら、
情報お待ちしてます。
長文失礼しました。
> WebBrowserコントロールには何故かgetElementsByNameメソッドが実装されていません。
> getElementByIDならあるのですが。

キーワードで検索すると良いですよ。
GetElementsByName で調べれば、これが見つかります。
http://msdn2.microsoft.com/ja-jp/library/system.windows.forms.htmlelementcollection.getelementsbyname.aspx
これは HtmlElementCollection のメソッドなので確かに HtmlDocument からは直接呼び出せません。でも、HtmlDocument 型には HtmlElementCollection 型のオブジェクトを返すプロパティがありますよね?
これくらいならわざわざ mshtml を使うまでもありません。
// COM を使うと色々面倒なのですよ。できる限り使わないのがベターです。
// 使わないのは使わないのでまた面倒な部分も多いですけどね……。

> mshtml.IHTMLDocument3 doc = (mshtml.IHTMLDocument3)wb.Document.DomDocument;
> doc.getElementsByName("test").item(a,0).
>
> とここまではよかったんですが、item(a,0).の.のあとにインテリセンスがValueを候補
> として挙げてくれません。無理やりitem(a,0).Value = "a";などと指定すると
> Object内にValueなんてないよと怒られます。

と言うことでここは問題じゃなくなりますが、解説を。
item メソッドの返値は Object 型です。つまり、「何か」でしかなく、それが具体的になんなのかをコンパイラは知ることができません。だから value とか言われても、Object 型にそんなプロパティはないのでどうしようもないのです。
コーディングしてる側がそれが具体的になんの型なのか理解しているなら、その型に明示的にキャストしてあげて下さい。


> mshtml.IHTMLDocument3 doc = (mshtml.IHTMLDocument3)wb.Document.DomDocument;
> mshtml.IHTMLElementCollection elms = (mshtml.IHTMLElementCollection)doc.getElementsByName("q");
> foreach (mshtml.IHTMLElement elm in elms)
> {
> elm.innerText = "a";
> }
>
> ここでも、例えばgetElementsByNameの引数をselectのものにしても、
> elm.としたときにselectedIndexは候補にでません。

IHTMLElement 型は、HTML の要素の基本となる型です。ですから、全ての要素が持つことができる innerHTML なんかはそのプロパティとして公開されています。
しかし、selectedIndex は普通の要素は持っていません。select 要素くらいしか持っていないでしょう。ですから、それは IHTMLSelectElement という、select 要素に特化した型がプロパティを持っています。
ここで、select 要素は IHTMLSelectElement ですが、同時にIHTMLElement でもあります。IHTMLElement は全ての HTML の要素を含みますからね。
さて、コンパイラは、elem が IHTMLElement であるとは理解します。しかし、コンパイラが理解できるのはそこまでです。はたしてこの要素が実際に何の要素なのか、実際に実行してみるまでは分かりません。取りあえず IHTMLElement であるとは言われているので、IHTMLElement でできる範囲の処理は行えますが、それ以上はどうにもなりません。
しかし、getElementsByTagName で select を選んだのなら、それに帰ってくるのが必ず IHTMLSelectElement を持っていることをプログラマは知っています。そこで、コンパイラに「これは IHTMLSelectElement だよ」と伝えるために、キャストを行うわけです。


ところで、.NET 2.0 の HTML DOM は、これらの特定の要素に特有の型というのを用意していません。全て HtmlElement クラスでまかないます。
では要素特有のプロパティやメソッドはどうするのか?
.NET 2.0 では、HtmlElement に直接実装されていない特有の操作を、 InvokeMember、GetAttribute/SetAttribute のいずれかのメソッドで行います。
例えば、ある HtmlElement の selectedIndex の取得をしたいのなら、
int index = (int)element.GetAttribute("selectedIndex");
というコードになります。
■No15892に返信(Hongliangさんの記事)

HongLiangさんこんなに詳しく解説して下さって、とても助かりました。
どうもありがとうございます。

>でも、HtmlDocument 型には HtmlElementCollection 型のオブジェクトを返すプロ
>パティがありますよね?

HtmlElement elm = (HtmlElement)wb.Document.getElementById("hoge");
HtmlElementCollection elms = elm.All;

こういうことですか?これでelms.getElementsByNameが使えるようになりました。

> .NET 2.0 では、HtmlElement に直接実装されていない特有の操作を、 InvokeMember、GetAttribute/SetAttribute のいずれかのメソッドで行います。
> 例えば、ある HtmlElement の selectedIndex の取得をしたいのなら、
> int index = (int)element.GetAttribute("selectedIndex");
> というコードになります。

できました!実はついさっきまでjavascriptでDOMを操作して無理やりやろうと
うなっておりました。

以下のコードでやりたいことができました。

WebBrowser wb = new WebBrowser();
...

//textフィールドにテキストを設定
HtmlElementCollection elms = (HtmlElementCollection)wb.Document.All.getElementsByName("text");
foreach (HtmlElement elm in elms)
{
  elm.InnerText = "a";
}

//セレクトメニューの特定のオプションを選択
elms = (HtmlElementCollection)wb.Document.All.getElementsByName("option1");
foreach (HtmlElement elm in elms)
{
  elm.SetAttribute("selected","selected");
}

//サブミットを実行
elms = (HtmlElementCollection)wb.Document.All.getElementsByName("submit");
foreach (HtmlElement elm in elms)
{
  elm.InvokeMember("click");
}
解決済み!

DOBON.NET | プログラミング道 | プログラミング掲示板