エディターで入力したHTMLソースコードをWebBrowserに反映させたい

解決


rain  2010-01-21 03:00:52  No: 37090

HTML用のソースコードエディターを作成しています。
特定のタイミング(「>」を入力した時等)でソースコードへの変更を、ファイルを介さずに反映させたいのですが、

1.
begin
OLEVariant(wb.Document as IHTMLDocument2).body.innerHTML:=Memo1.Lines.Text;
end;
<body bgcolor="〜">等を入力しても反映されませんでした。

2.
var
ms:TMemoryStream;
begin
if Assigned(wb.Document) then
begin
ms:=TMemoryStream.Create;
try
memo1.Lines.SaveToStream(ms);
ms.Seek(0,0);
(wb.Document as IPersistStreamInit).Load(TStreamAdapter.Create(ms));
except
end;
end;
end;
反映は上手くいくのですが、一々フォーカスがブラウザに移ってしまい入力が不便です。

2の方法でMemo1.SetFocusを使ってみたのですが、フォーカスはMemo1に移らずWebBrowserが所持したままでした。
一時ファイルを利用せずに、Memo1への特定キーの入力があったときだけ反映させ、さらに反映後にMemo1がフォーカスを持つにはどのようなコードにすればよいでしょうか?


rain  2010-01-21 03:14:17  No: 37091

すみません、2点訂正と補足です。

エディターで〜→「TMemoで」です。
コード内「wb」=TWebBrowserです。

よろしくお願いします。


jazzin  2010-01-21 04:24:29  No: 37092

1の方法はどちらかというとHTMLやJavaScriptの領域になると思うのですが、
innerHTMLというのはタグの内側を参照する関数なので、
そのようなコードを書いた場合、

<body><body bgcolor="〜"></body>

という結果になってしまい、当然ながら反映されません。
bodyタグの属性だけは別で入力できるようにする等、回避策を取ればその方法でも問題ないとは思います。
(例えばIHTMLDocument2(wb.Document).body.background := Edit1.Text;など)

2の方法でフォーカスを再取得するには、OnDocumentCompleteイベント内でSetFocusすれば可能だと思われます。


rain  2010-01-21 05:23:18  No: 37093

>jazzinさん
やはりinnerHTMLでは無理ですか…

2の方法で、
procedure TForm1.wbDocumentComplete(Sender: TObject;
  const pDisp: IDispatch; var URL: OleVariant);
begin
OLEVariant(wb.Document as IHTMLDocument2).ParentWindow.scroll(beforeX,beforeY);
Memo1.SetFocus;
end;

と、上記のOLEVariantの行をコメントアウトした場合とで試してみたのですが、どちらもフォーカスはWebBrowserにありました。
2のコードはMemo1.OnKeyUpに書いてあるのですが、これが何か邪魔をしているのでしょうか?
引き続き、お願いいたします。


jazzin  2010-01-21 23:37:44  No: 37094

別の方法になりますが、これではどうでしょうか?

OleVariant(wb.Document).close;
OleVariant(wb.Document).clear;
OleVariant(wb.Document).write('<html><body bgcolor="red">test</body></html>');


rain  2010-01-22 10:49:41  No: 37095

>jazzinさん
コピペして試してみたのですが、やはり駄目でした。
何度か試してみた結果、どうもWebBrowserを1度でもスクロールすると、最上部を表示していてもフォーカスをかっさらっていってしまうようです。
スクロール位置を更新前の位置にする為の処理を削除しても同じ結果になってしまうのでどうにも対処法がわかりませんでした。


rain  2010-01-22 14:59:21  No: 37096

色々試してようやく自己解決出来たので報告です。
単語がわからないのであやふやな説明になりますが、
どうやらフォーカスはWebBrowserの中の子フォーム(というのでしょうか?もしくはページ?)
で取られ(仮にAとします)、このフォーカスを所持しているAから、
同フォーム内の別のコンポーネントへの直接のフォーカス移動は出来ないようでした。
駄目元でMemo1.OnKeyUpイベント内にMemo1.SetFocusを置き、直前にwb.SetFocus(=WebBrowser1.SetFocus)を置いた所期待していた動作になりました。
最初のjazzinさんの書き込みの通りにOnDocumentCompleteイベント内で同じように
wb.SetFocus
Memo1.SetFocus
と書いても同じように処理されたので、
結局はWebBrowserコンポーネントにフォーカスをあてるという作業をしていなかった事が問題だったようです。

結局は自己解決となりましたが、お知恵を拝借出来てとても助かりました。
ありがとうございました。


rain  2010-01-22 15:00:23  No: 37097

すみません、お知恵をお借りしている時点で自己解決ではないですね。
言葉を間違えたこと、謝罪いたします。
改めて、解決出来た報告と御礼申し上げます。
ありがとうございました。


jazzin  2010-01-23 00:24:14  No: 37098

すみません、こちらも言葉が足りませんでした。
最後の書き込みはbodyタグを反映させる方法(1の方法の代替)です。

また、フォーカスの問題についても少し調べてみたのですが、
これはどちらかというとVCLのバグと言った方が良いかもしれません。
VCLは内部でも特定のコントロールにフォーカスがあるかどうかの状態を持っており、
この状態がFalseでSetFocusが呼ばれた時に、Win32APIのSetFocusを呼び出します。

一方、WebBrowserはこの内部状態を変更せずにフォーカスを奪ってしまうため、
内部的にはMemo1がフォーカスを持ったままのような状態になってしまい、正しく処理されないようです。
ですので、何でもいいので元のコントロール以外に一度フォーカスを移し、
再度元のコントロールにフォーカスをあてることで内部状態が正しくなり、フォーカスが取得できます。
wb.SetFocusを実行した後にMemo1.SetFocusを実行すると正常に処理されるのはこのためです。
試しにTButtonを置き、Button1.SetFocus;Memo1.SetFocusとすることでも同じ効果が得られました。

以上、蛇足ですが参考までに。


rain  2010-01-23 01:32:42  No: 37099

>jazzinさん
詳しい情報をありがとうございます。
データ上:フォーカスはMemo1
実際    :フォーカスはWebBrowser1
となっていて、データ上ではMemo1がフォーカスを持っている為にSetFocusを呼び出しても動作を行わなかった
という事でしょうか。
TWebBrowserはHTMLファイルの表示以外の用途で使うとなるとどうにも使いにくい感じがしますね…

大変勉強になりました。
テストまでしていただいてありがとうございました。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加