用WebBrowser實現HTML介面的應用和互動
這一篇將繼續討論在使用delphi進行普通應用程式開發的時候,WebBrowser的具體應用,主要是針對使用其進行HTML介面開發的,這也是一篇我在網上找到的資料,大家如要轉載,請尊重原作者的智慧財產權,務必著名出處.
什麼應用需要HTML的介面
HTML的介面有以下特點:圖文混排,格式靈活,可以包含Flash、聲音和視訊等,實現圖文聲像的多媒體介面,而且易於建立和維護。另外,HTML的顯示環境一般機器上都具備,通常不需要安裝額外的軟體。當然,HTML介面也有它欠缺的方面,即:介面控制能力有限,程式碼除錯不便----雖然DHTML提供了比較強的程式設計特性,但是比起Delphi的傳統的開發語言和工具來,對介面的控制能力,尤其是和資料互動時的控制能力還是稍遜一籌。
瞭解了這些特點,我們就可以在實際應用開發中,適時地選擇HTML技術。下面舉個例子:
一種儀器的管理程式,需要顯示該儀器的操作方法文件,包含文字和圖片,並要求可以隱藏或顯示文件,並能安要求列印。
這個應用中,圖文顯示、隱藏/顯示部分文件、圖文列印等需求,都是HTML介面所擅長的,用傳統的表單控制元件實現幾乎無法想像。
用什麼實現HTML的介面
用Delphi實現HTML介面的應用主要有兩種選擇:WebBrowser Control或MSHTML。為了弄清兩者如何選擇,我們先來看看Internet Exporer 4.0及其後續版本的體系結構:
IE瀏覽器是建立在SHDOCVW.DLL元件之上的,而SHDOCVW.DLL則建立在MSHTML.DLL元件之上,底層則包括指令碼引擎等。SHDOCVW.DLL提供了對活動文件(Active Document)的支援----例如Word等文件可以在IE中顯示,並提供導航、in-place*連線、收藏夾、瀏覽歷史和分級內容選擇(PICS: Platform for Internet Content Selection)等功能。SHDOCVW.DLL元件雖然也提供了很多介面可以單獨使用,但是通常所指的SHDOCVW.DLL就是WebBrowser Control。MSHTML.DLL是實行HTML解析和表現的元件。它通過DHTML物件模型提供對HTML文件的訪問。它實現了活動文件伺服器介面,可以通過COM介面呼叫。
不難看出,WebBrowser在比較高的層次上,提供了更為豐富的功能,因此一般通常程式設計都採用WebBrower控制元件。MSHTML只有在需要解析HTML這樣的特殊應用中,才推薦使用。微軟的MSDN網站上提供了一個使用MSHTML的例子:WalkAll Sample Source Page。
(*注:In-place連結,是指點選HTML連線時,在相同的WebBrowser例項中顯示連線的HTML文件。如果僅使用MSHTML.DLL,點選連結將導致在新的瀏覽器例項中開啟連結的文件。)
如何訪問HTML頁面的內容
首先,在Delphi 7.0元件皮膚的Internet頁上,把TWebBrowser元件放到表單上。
通過執行以下語句裝載HTML文件到WebBrowser中進行顯示:
WebBrowser1.Navigate(GetCurrentDir + '\index.htm');
隱藏/顯示HTML元件程式碼示例:
var
Doc : IHTMLDocument2;
element: IHTMLElement;
begin
Doc := IHTMLDocument2(WebBrowser1.Document);
if nil <> Doc then
begin
element := Doc.all.item('T1', 0) as IHTMLElement;
if nil <> element then begin
if '' = element.style.display then
element.style.display := 'none'
else
element.style.display := '';
end;
end;
end;
設定/取值程式碼示例:
var
Doc : IHTMLDocument2;
inputText : IHTMLInputTextElement;
begin
Doc := IHTMLDocument2(WebBrowser1.Document);
if nil <> Doc then
begin
//如果T1不是IHTMLInputTextElement型別將出錯
inputText := Doc.all.item('T1', 0) as IHTMLInputTextElement;
inputText.value := Edit1.Text;
Edit2.Text := inputText.value;
end;
end;
提示:關於哪些HTML元件(標記)應該採用什麼MSHTML介面進行訪問,請參考MSDN Library中的Web Development > Programming and Reusing the Browser > MSHTML Reference > Interfaces and Scripting Objects。
如何呼叫Javascript函式(兼談訊息提示框)
知道了訪問HTML內容的方法,就可以通過間接方式呼叫HTML頁面上包含的Javascript程式碼。具體實現方式是:在HTML中插入<span></span>等不可見元件,利用它的click事件呼叫響應的Javascript函式,然後再Delphi中呼叫該元件的click過程。
下面我們就用Delphi呼叫Javascript的alert函式來實現訊息提示框。首先在HTML中加入:
<span id="ShowMessage" style="display:none" onclick="alert(this.innerText);"></span>
Delphi中的呼叫程式碼如下:
procedure TForm1.Alert(const Msg : string);
var
Doc : IHTMLDocument2;
Element : IHTMLElement;
begin
Doc := IHTMLDocument2(WebBrowser1.Document);
Assert(nil <> Doc);//一定要先載入HTML
Element := Doc.all.item('ShowMessage', 0) as IHTMLElement;
if nil <> Element then
begin
Element.innerText := Msg;
Element.click;
end;
end;
我發現在Delphi中用Browser顯示HTML,如果你的表單是作為EXE執行,然後嵌入到了別的表單的元件上顯示的,例如,Form1.Parent := Form2.Panel1,即Form1顯示在Form2中Panel1所佔據的位置,當你用ShowMessage顯示提示資訊時,HTML的內容依然可以被操作,這顯然不太好。使用Javascript中的alert函式則可避免這種現象。
如何禁止右鍵選單(如何禁止使用者檢視原始碼)
預設情況下,在顯示HTML的WebBrowser上點選滑鼠右鍵,會顯示一個彈出選單,和IE中看到的一樣。通過這個選單使用者可以檢視HTML的原始碼。因此有時候我們需要遮蔽該選單。和該選單相關的介面是IEDocHostUIHandler。已經用人對它進行了封裝,詳見ieConst.pas 和 IEDocHostUIHandler.pas。使用方法如下:
var
Form1: TForm1;
FDocHostUIHandler: TDocHostUIHandler;
...
implementation
...
procedure TForm1.FormCreate(Sender: TObject);
begin
FDocHostUIHandler := TDocHostUIHandler.Create;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
FDocHostUIHandler.Free;
end;
procedure TForm1.WebBrowser1NavigateComplete2(Sender: TObject;
pDisp: IDispatch; var URL: OleVariant);
var
hr: HResult;
CustDoc: ICustomDoc;
begin
hr := WebBrowser1.Document.QueryInterface(ICustomDoc, CustDoc);
if hr = S_OK then
CustDoc.SetUIHandler(FDocHostUIHandler);
end;
有時你可能還需要定製自己的右鍵選單,這是還是要藉助於IEDocHostUIHandler,具體實現方法可以看看MSDN Library。
如何響應HTML的事件(如何在HTML中呼叫Delphi的程式碼)
HTML事件的響應方式有兩種:一種是Javascript,一種是在Delphi中響應。一些簡單的功能可以在Javascript中實現,這樣易於修改。但是從功能、安全性等方面考慮,通常還是要在Delphi中實現。例如當使用者點選HTML上的一個按鈕時,需要訪問資料庫,這是就得用Delphi了。
在Delphi中響應HTML事件,實際上就是響應ActiveX事件的問題,這通過事件槽(Event Sink)來實現,有些繁瑣。還好前人已經為我們作了很多工作。利用Experts Exchange網站的Cynna封裝的TDHTMLEvent類(該原始碼請看本文的附件),實現就簡單多了。實現程式碼如下:
var
Form1: TForm1;
EventSink: TDHTMLEvent;
...
implementation
...
procedure TForm1.FormCreate(Sender: TObject);
begin
EventSink:= TDHTMLEvent.Create;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
EventSink.Free;
end;
procedure TForm1.DemoEventSink(Sender: TObject);
begin
ShowMessage('成功從HTML中呼叫Delphi的過程。');
end;
procedure TForm1.WebBrowser1DocumentComplete(Sender: TObject;
const pDisp: IDispatch; var URL: OleVariant);
var
Doc : IHTMLDocument2;
Element : IHTMLElement;
begin
Doc := IHTMLDocument2(WebBrowser1.Document);
if nil <> Doc then
begin
//找到HTML元件
Element := Doc.all.item('B3', 0) as IHTMLElement;
//使HTML元件的click事件和DemoEventSink過程關連
Element.onclick := EventSink.HookEventHandler(DemoEventSink);
end;
end;
點選HTML頁面中ID為'B3'的按鈕,就會呼叫DemoEventSink過程。
如何能在HTML控制元件上輸入回車
含有多行文字輸入框(textarea )或提交(submit)按鈕的HTML表單在TWebBrowser中顯示時,對Enter鍵不響應。另外,Delphi表單上按鈕的快捷字母鍵也無法在HTML表單上輸入,因為一輸入就觸發相應按鈕的單擊事件。解決程式碼如下:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, OleCtrls, SHDocVw_TLB, ActiveX, StdCtrls;
type
TForm1 = class(TForm)
WebBrowser1: TWebBrowser;
Button1: TButton;
Button2: TButton;
procedure FormDestroy(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
FOleInPlaceActiveObject: IOleInPlaceActiveObject;
procedure MsgHandler(var Msg: TMsg; var Handled: Boolean);
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormDestroy(Sender: TObject);
begin
FOleInPlaceActiveObject := nil;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Application.OnMessage := MsgHandler;
end;
procedure TForm1.MsgHandler(var Msg: TMsg; var Handled: Boolean);
const
DialogKeys: set of Byte = [VK_LEFT, VK_RIGHT, VK_BACK, VK_UP, VK_DOWN,
$30..$39, $41..42, $44..$55, $57, $59..$5A];
var
iOIPAO: IOleInPlaceActiveObject;
Dispatch: IDispatch;
begin
{ exit if we don't get back a webbrowser object }
if (WebBrowser1 = nil) then
begin
Handled := System.False;
Exit;
end;
Handled := (IsDialogMessage(WebBrowser1.Handle, Msg) = System.True);
if (Handled) and (not WebBrowser1.Busy) then
begin
if FOleInPlaceActiveObject = nil then
begin
Dispatch := WebBrowser1.Application;
if Dispatch <> nil then
begin
Dispatch.QueryInterface(IOleInPlaceActiveObject, iOIPAO);
if iOIPAO <> nil then
FOleInPlaceActiveObject := iOIPAO;
end;
end;
if FOleInPlaceActiveObject <> nil then
if ((Msg.message = WM_KEYDOWN) or (Msg.message = WM_KEYUP)) and
(Msg.wParam in DialogKeys) then
// nothing - do not pass on the DialogKeys
else
FOleInPlaceActiveObject.TranslateAccelerator(Msg);
end;
end;
initialization
OleInitialize(nil);
finalization
OleUninitialize;
本段程式碼出自SwissDelphiCenter.ch,作者未知。主要要引用ActiveX。Delphi 7中SHDocVw_TLB改為SHDocVw。
如何實現HTML的列印和預覽
HTML的列印和預覽向來是個難題,但自從IE5.5推出後,情況大有改觀。你可以利用其“列印模板”功能,實現自己的預覽視窗和控制列印。“列印模板”的使用方法請參考MSDN Library中的Web Development > Programming and Reusing the Browser > Print Templates目錄下的文章。從微軟的網站上還可以下載到一個不錯的例子,示例如何一步步由淺入深地使用Print Template (下載:列印模板示例)。
你會發現,要自己實現一個功能完善的列印模板也並非易事。IE瀏覽器本身帶的列印模板做得還不錯,能否在它的基礎上加上自己的定製功能呢?答案是肯定的,至少從技術上看是這樣(不考慮版權問題)。下面就介紹這偷懶的招。
用Visual Studio開啟x:\Program Files\Internet Explorer\MUI\0804\SHDOCLC.DLL,會看到其資源目錄。其中HTML/PREVIEW.DLG就是IE所帶的列印模板了。把它export(匯出)出來,把副檔名改成HTM,開啟看看,是不是特刺激?PREVIEW.DLG用到了幾個圖片檔案,在2110目錄下,別忘了匯出。(注:我的環境是Windows XP Professional英文版+SP1a,IE是6.0sp1。)
IE預設的模版中,頁首頁尾均只支援純文字。下面以定製HTML頁首為例,看看如何定製自己的列印模板。思路是:用自己的頁首內容換掉原有的內容,並修改其頁首高度和頁邊距使之和新的頁首相對應。
第一步,定義頁首。在要使用此模版預覽列印的HTML檔案中加入一個id為Header的div標記,括起HTML頁首內容,並制定以英寸為單位的頁首的高度和寬度,其中寬度應該和模版相符。例:
<div id="Header" style="DISPLAY:none; WIDTH:6.5in; HEIGHT:0.78in">
...(HTML頁首內容)
</div>
第二步,宣告變數。在模版前面變數宣告部分加上兩個變數宣告:
var g_htmlHeader = "";//用於儲存頁首內容
var g_nHeaerHeight = 0;//頁首的高度
第三步,取得頁首。在函式OnLoadBody()中的“Printer.footer = dialogArguments.__IE_FooterString”語句之後加入這段程式碼:
oPageHeader = dialogArguments.__IE_BrowseDocument.all.item("Header", 0);
if (null != oPageHeader)
{
g_htmlHeader = oPageHeader.innerHTML;
g_nHeaerHeight = oPageHeader.style.posHeight;
}
第四步,指定頁邊距和頁首高度。在上面的程式碼下面緊接著加入:
//指定頁邊距。其中40可以自己改,單位是百分之一英寸。
Printer.marginTop = 40 + (g_nHeaerHeight * 100);
Printer.marginBottom = 40;
Printer.marginLeft = 40;
Printer.marginRight = 40;
在函式EnsureDocuments()中,
/*註釋掉以下程式碼
if (header)
{
tmp = upTop + (27 / 100);
if (tmp > top)
top = tmp;
}
if (footer)
{
tmp = upBottom + (27 / 100);
if (tmp > bottom)
bottom = tmp;
}
*/
//緊接著加上:
tmp = upTop + g_nHeaerHeight;
if (tmp > top)
top = tmp;
//下面隔幾行,註釋掉:oRule.style.top = upTop + "in";
第五步,指定頁首內容。在函式CPrintDoc_AddPage()中,在“HeadFoot.page = HeadFoot.pageTotal;”語句之後加入:
//這兩行用於設定頁碼,你在頁首可以通過加入“[P]”和“[p]”分別代表總頁數和當前頁數。
g_htmlHeader = g_htmlHeader.replace("[P]", "<span class='hfPageTotal'></span>");
var pageHeader = g_htmlHeader.replace("[p]", HeadFoot.pageTotal);
//下面隔3行,註釋掉:
//~oPage.children("header").innerHTML = HeadFoot.HtmlHead;
//~oPage.children("footer").innerHTML = HeadFoot.HtmlFoot;
//下面隔幾行,把“newHTM += HeadFoot.HtmlHead;”改為:
newHTM += pageHeader ;
//然後註釋掉(不要頁尾):newHTM += HeadFoot.HtmlFoot;
至此,一個支援自定義HTML頁首的新模版就定製完成了。是不是覺得特爽?如果覺得它給你省下了兩週的時間,就趕緊到“希望之光”網站上,花你2天的工資,資助一個小孩上學吧。
定製好的列印模板怎麼用呢?請看以下程式碼:
var
vaIn, vaOut: OleVariant;
CmdTarget : IOleCommandTarget;
MyHandle : THandle;
begin
vaIn := 'c:\\Preview.htm';
//預覽方法1:WebBrowser1.ControlInterface.ExecWB(OLECMDID_PRINTPREVIEW, OLECMDEXECOPT_PROMPTUSER, vaIn, vaOut);
//下面是方法2:
if WebBrowser1.Document <> nil then
begin
WebBrowser1.Document.QueryInterface(IOleCommandTarget, CmdTarget);
if CmdTarget <> nil then
begin
try
CmdTarget.Exec( PGuid(nil), OLECMDID_PRINTPREVIEW, OLECMDEXECOPT_PROMPTUSER, vaIn, vaOut);
finally
CmdTarget._Release;
end;
end
else
begin
ShowMessage('IE不支援該功能,請升級至IE5.5以上。');
end;
end;
end;
方法1簡潔,但是如果WebBroswer不支援列印預覽的話就會出錯。第二種方法可能更好一些。
在列印預覽時,預覽視窗的尺寸大小總是和WebBrowser所在的Form的一樣,而且沒法最大化。更麻煩的是,如果你的表單是嵌入到了別的表單的元件上顯示的,例如,Form1.Parent := Form2.Panel1,即Form1顯示在Form2中Panel1所佔據的位置,那麼預覽視窗就變得很小了,不拉大根本沒法看。解決辦法如下,在預覽的程式碼後面加上以下程式碼,使預覽視窗最大化:
Handle:=FindWindow('Internet Explorer_TridentDlgFrame', '列印預覽');
if 0 <> MyHandle then
begin
ShowWindow(MyHandle , SW_MAXIMIZE);
end;
如果不預覽而是直接列印,則把OLECMDID_PRINTPREVIEW換成OLECMDID_PRINT就可以了。
如果要在Web應用中使用列印模板,可以通過ActiveX來實現呼叫。
注:列印模板需要安裝Internet Explorer 5.5以上版本,本文其它功能需要安裝Internet Explorer4.0以上版本。
如何打包HTML和相關檔案
應用做好了,總不能把HTML檔案和相關的圖片檔案等直接釋出吧。這樣既不安全,前面禁止使用者檢視原始碼的努力也白費了。因此至少應該將這些檔案打個包。一般來說,作為資源編譯到exe或dll裡就行了。我覺得編譯到DLL中最為方便。在Visual Studio中,新建一個Win32工程,應用型別選擇DLL。然後把HTML檔案和相關的圖片檔案等資源加到工程中,然後編譯即可。
再新增HTML等資源時,我強烈推薦用手工加入的方法。原因有二:一,GIF等圖片檔案加入到工程中時,Studio可能會把檔案內容自動改了,使得該檔案不能正確顯示;二,加入資源後會自動生成資源ID,需要把它改成你需要的名稱(通常改成和檔名相同),當檔案很多時,這項工作就很浪費時間,也很煩人。手工加入,即用文字編輯器把資源指令碼檔案(工程名.rc)開啟,手工加入內容。我就不贅述了,格式例子如下:
About.htm HTML "HTML\\About.htm"
image016.gif IMAGES "HTML\\images\\image016.gif"
當加入很多檔案時,如何節省時間呢?沒有實踐經驗的人,是不可能想到這些問題的。彆著急,按我說的做。
首先,進入命令列(DOS)介面(Windows NT/2000/XP/2003下執行cmd.exe進入),進入你的HTML等資原始檔所在的目錄,執行“dir > temp.txt”,把檔案列表輸出到temp.txt。
接著,用文字編輯器把該檔案開啟,去掉頭尾內容,僅留檔案列表部分,例如:
2004-03-17 11:20 20,397 About.htm
2004-03-17 11:20 27,397 index.htm
然後,用Excel把修改後的檔案開啟。開啟時,“原始資料型別”請選擇“固定寬度 - 每列欄位加空格對齊”。這樣,日期、時間、檔案大小、檔名就被分別放在了不同的列中。刪除前三列,僅留檔名一列,並把該列複製一份。在兩個檔名列之間插入兩個空列,分別填寫“HTML”和“"HTML\\”,然後就可以另存成以製表符分隔的文字檔案了。
最後,用文字編輯器把上一步處理好的檔案開啟,不用我多說,只要幾個替換,就得到所需要的資源指令碼了。對於不同目錄下的檔案,均需要這麼弄以下。
資源指令碼弄好了,把資原始檔也加入(不是作為資源加入)工程,編譯,就得到打包好的DLL檔案了。
接下來的問題是,這個DLL怎麼用啊?別急,WebBrowser支援一種叫res的協議,可以訪問檔案裡的資源。
例如,假設上面About.htm打包到了myresource.dll檔案中,則可以通過res://myresource.dll/About.htm訪問,image016.gif則可通過res://myresource.dll/images/image016.gif訪問(注意到了吧,HTML在根目錄下,而IMAGES等其它資源則在同名目錄下)。
如果About.htm中通過“images/image016.gif”引用了image016.gif檔案,則該圖片在WebBrowser中正常顯示。換句話說,你在打包之前,程式可以通過file://...訪問HTML,打包之後,只需要換成res://...就可以了----打包對程式和HTML幾乎沒什麼影響。但是,切記,切記!千萬不要僅以數字來做檔名(如:1.htm、2.gif等),因為數字是被用來標識某種資源或某個資源的,如果用僅用數字作檔名(可以用字母+數字),打包後會導致訪問找不到檔案。
什麼應用需要HTML的介面
HTML的介面有以下特點:圖文混排,格式靈活,可以包含Flash、聲音和視訊等,實現圖文聲像的多媒體介面,而且易於建立和維護。另外,HTML的顯示環境一般機器上都具備,通常不需要安裝額外的軟體。當然,HTML介面也有它欠缺的方面,即:介面控制能力有限,程式碼除錯不便----雖然DHTML提供了比較強的程式設計特性,但是比起Delphi的傳統的開發語言和工具來,對介面的控制能力,尤其是和資料互動時的控制能力還是稍遜一籌。
瞭解了這些特點,我們就可以在實際應用開發中,適時地選擇HTML技術。下面舉個例子:
一種儀器的管理程式,需要顯示該儀器的操作方法文件,包含文字和圖片,並要求可以隱藏或顯示文件,並能安要求列印。
這個應用中,圖文顯示、隱藏/顯示部分文件、圖文列印等需求,都是HTML介面所擅長的,用傳統的表單控制元件實現幾乎無法想像。
用什麼實現HTML的介面
用Delphi實現HTML介面的應用主要有兩種選擇:WebBrowser Control或MSHTML。為了弄清兩者如何選擇,我們先來看看Internet Exporer 4.0及其後續版本的體系結構:
IE瀏覽器是建立在SHDOCVW.DLL元件之上的,而SHDOCVW.DLL則建立在MSHTML.DLL元件之上,底層則包括指令碼引擎等。SHDOCVW.DLL提供了對活動文件(Active Document)的支援----例如Word等文件可以在IE中顯示,並提供導航、in-place*連線、收藏夾、瀏覽歷史和分級內容選擇(PICS: Platform for Internet Content Selection)等功能。SHDOCVW.DLL元件雖然也提供了很多介面可以單獨使用,但是通常所指的SHDOCVW.DLL就是WebBrowser Control。MSHTML.DLL是實行HTML解析和表現的元件。它通過DHTML物件模型提供對HTML文件的訪問。它實現了活動文件伺服器介面,可以通過COM介面呼叫。
不難看出,WebBrowser在比較高的層次上,提供了更為豐富的功能,因此一般通常程式設計都採用WebBrower控制元件。MSHTML只有在需要解析HTML這樣的特殊應用中,才推薦使用。微軟的MSDN網站上提供了一個使用MSHTML的例子:WalkAll Sample Source Page。
(*注:In-place連結,是指點選HTML連線時,在相同的WebBrowser例項中顯示連線的HTML文件。如果僅使用MSHTML.DLL,點選連結將導致在新的瀏覽器例項中開啟連結的文件。)
如何訪問HTML頁面的內容
首先,在Delphi 7.0元件皮膚的Internet頁上,把TWebBrowser元件放到表單上。
通過執行以下語句裝載HTML文件到WebBrowser中進行顯示:
WebBrowser1.Navigate(GetCurrentDir + '\index.htm');
隱藏/顯示HTML元件程式碼示例:
var
Doc : IHTMLDocument2;
element: IHTMLElement;
begin
Doc := IHTMLDocument2(WebBrowser1.Document);
if nil <> Doc then
begin
element := Doc.all.item('T1', 0) as IHTMLElement;
if nil <> element then begin
if '' = element.style.display then
element.style.display := 'none'
else
element.style.display := '';
end;
end;
end;
設定/取值程式碼示例:
var
Doc : IHTMLDocument2;
inputText : IHTMLInputTextElement;
begin
Doc := IHTMLDocument2(WebBrowser1.Document);
if nil <> Doc then
begin
//如果T1不是IHTMLInputTextElement型別將出錯
inputText := Doc.all.item('T1', 0) as IHTMLInputTextElement;
inputText.value := Edit1.Text;
Edit2.Text := inputText.value;
end;
end;
提示:關於哪些HTML元件(標記)應該採用什麼MSHTML介面進行訪問,請參考MSDN Library中的Web Development > Programming and Reusing the Browser > MSHTML Reference > Interfaces and Scripting Objects。
如何呼叫Javascript函式(兼談訊息提示框)
知道了訪問HTML內容的方法,就可以通過間接方式呼叫HTML頁面上包含的Javascript程式碼。具體實現方式是:在HTML中插入<span></span>等不可見元件,利用它的click事件呼叫響應的Javascript函式,然後再Delphi中呼叫該元件的click過程。
下面我們就用Delphi呼叫Javascript的alert函式來實現訊息提示框。首先在HTML中加入:
<span id="ShowMessage" style="display:none" onclick="alert(this.innerText);"></span>
Delphi中的呼叫程式碼如下:
procedure TForm1.Alert(const Msg : string);
var
Doc : IHTMLDocument2;
Element : IHTMLElement;
begin
Doc := IHTMLDocument2(WebBrowser1.Document);
Assert(nil <> Doc);//一定要先載入HTML
Element := Doc.all.item('ShowMessage', 0) as IHTMLElement;
if nil <> Element then
begin
Element.innerText := Msg;
Element.click;
end;
end;
我發現在Delphi中用Browser顯示HTML,如果你的表單是作為EXE執行,然後嵌入到了別的表單的元件上顯示的,例如,Form1.Parent := Form2.Panel1,即Form1顯示在Form2中Panel1所佔據的位置,當你用ShowMessage顯示提示資訊時,HTML的內容依然可以被操作,這顯然不太好。使用Javascript中的alert函式則可避免這種現象。
如何禁止右鍵選單(如何禁止使用者檢視原始碼)
預設情況下,在顯示HTML的WebBrowser上點選滑鼠右鍵,會顯示一個彈出選單,和IE中看到的一樣。通過這個選單使用者可以檢視HTML的原始碼。因此有時候我們需要遮蔽該選單。和該選單相關的介面是IEDocHostUIHandler。已經用人對它進行了封裝,詳見ieConst.pas 和 IEDocHostUIHandler.pas。使用方法如下:
var
Form1: TForm1;
FDocHostUIHandler: TDocHostUIHandler;
...
implementation
...
procedure TForm1.FormCreate(Sender: TObject);
begin
FDocHostUIHandler := TDocHostUIHandler.Create;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
FDocHostUIHandler.Free;
end;
procedure TForm1.WebBrowser1NavigateComplete2(Sender: TObject;
pDisp: IDispatch; var URL: OleVariant);
var
hr: HResult;
CustDoc: ICustomDoc;
begin
hr := WebBrowser1.Document.QueryInterface(ICustomDoc, CustDoc);
if hr = S_OK then
CustDoc.SetUIHandler(FDocHostUIHandler);
end;
有時你可能還需要定製自己的右鍵選單,這是還是要藉助於IEDocHostUIHandler,具體實現方法可以看看MSDN Library。
如何響應HTML的事件(如何在HTML中呼叫Delphi的程式碼)
HTML事件的響應方式有兩種:一種是Javascript,一種是在Delphi中響應。一些簡單的功能可以在Javascript中實現,這樣易於修改。但是從功能、安全性等方面考慮,通常還是要在Delphi中實現。例如當使用者點選HTML上的一個按鈕時,需要訪問資料庫,這是就得用Delphi了。
在Delphi中響應HTML事件,實際上就是響應ActiveX事件的問題,這通過事件槽(Event Sink)來實現,有些繁瑣。還好前人已經為我們作了很多工作。利用Experts Exchange網站的Cynna封裝的TDHTMLEvent類(該原始碼請看本文的附件),實現就簡單多了。實現程式碼如下:
var
Form1: TForm1;
EventSink: TDHTMLEvent;
...
implementation
...
procedure TForm1.FormCreate(Sender: TObject);
begin
EventSink:= TDHTMLEvent.Create;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
EventSink.Free;
end;
procedure TForm1.DemoEventSink(Sender: TObject);
begin
ShowMessage('成功從HTML中呼叫Delphi的過程。');
end;
procedure TForm1.WebBrowser1DocumentComplete(Sender: TObject;
const pDisp: IDispatch; var URL: OleVariant);
var
Doc : IHTMLDocument2;
Element : IHTMLElement;
begin
Doc := IHTMLDocument2(WebBrowser1.Document);
if nil <> Doc then
begin
//找到HTML元件
Element := Doc.all.item('B3', 0) as IHTMLElement;
//使HTML元件的click事件和DemoEventSink過程關連
Element.onclick := EventSink.HookEventHandler(DemoEventSink);
end;
end;
點選HTML頁面中ID為'B3'的按鈕,就會呼叫DemoEventSink過程。
如何能在HTML控制元件上輸入回車
含有多行文字輸入框(textarea )或提交(submit)按鈕的HTML表單在TWebBrowser中顯示時,對Enter鍵不響應。另外,Delphi表單上按鈕的快捷字母鍵也無法在HTML表單上輸入,因為一輸入就觸發相應按鈕的單擊事件。解決程式碼如下:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, OleCtrls, SHDocVw_TLB, ActiveX, StdCtrls;
type
TForm1 = class(TForm)
WebBrowser1: TWebBrowser;
Button1: TButton;
Button2: TButton;
procedure FormDestroy(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
FOleInPlaceActiveObject: IOleInPlaceActiveObject;
procedure MsgHandler(var Msg: TMsg; var Handled: Boolean);
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormDestroy(Sender: TObject);
begin
FOleInPlaceActiveObject := nil;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Application.OnMessage := MsgHandler;
end;
procedure TForm1.MsgHandler(var Msg: TMsg; var Handled: Boolean);
const
DialogKeys: set of Byte = [VK_LEFT, VK_RIGHT, VK_BACK, VK_UP, VK_DOWN,
$30..$39, $41..42, $44..$55, $57, $59..$5A];
var
iOIPAO: IOleInPlaceActiveObject;
Dispatch: IDispatch;
begin
{ exit if we don't get back a webbrowser object }
if (WebBrowser1 = nil) then
begin
Handled := System.False;
Exit;
end;
Handled := (IsDialogMessage(WebBrowser1.Handle, Msg) = System.True);
if (Handled) and (not WebBrowser1.Busy) then
begin
if FOleInPlaceActiveObject = nil then
begin
Dispatch := WebBrowser1.Application;
if Dispatch <> nil then
begin
Dispatch.QueryInterface(IOleInPlaceActiveObject, iOIPAO);
if iOIPAO <> nil then
FOleInPlaceActiveObject := iOIPAO;
end;
end;
if FOleInPlaceActiveObject <> nil then
if ((Msg.message = WM_KEYDOWN) or (Msg.message = WM_KEYUP)) and
(Msg.wParam in DialogKeys) then
// nothing - do not pass on the DialogKeys
else
FOleInPlaceActiveObject.TranslateAccelerator(Msg);
end;
end;
initialization
OleInitialize(nil);
finalization
OleUninitialize;
本段程式碼出自SwissDelphiCenter.ch,作者未知。主要要引用ActiveX。Delphi 7中SHDocVw_TLB改為SHDocVw。
如何實現HTML的列印和預覽
HTML的列印和預覽向來是個難題,但自從IE5.5推出後,情況大有改觀。你可以利用其“列印模板”功能,實現自己的預覽視窗和控制列印。“列印模板”的使用方法請參考MSDN Library中的Web Development > Programming and Reusing the Browser > Print Templates目錄下的文章。從微軟的網站上還可以下載到一個不錯的例子,示例如何一步步由淺入深地使用Print Template (下載:列印模板示例)。
你會發現,要自己實現一個功能完善的列印模板也並非易事。IE瀏覽器本身帶的列印模板做得還不錯,能否在它的基礎上加上自己的定製功能呢?答案是肯定的,至少從技術上看是這樣(不考慮版權問題)。下面就介紹這偷懶的招。
用Visual Studio開啟x:\Program Files\Internet Explorer\MUI\0804\SHDOCLC.DLL,會看到其資源目錄。其中HTML/PREVIEW.DLG就是IE所帶的列印模板了。把它export(匯出)出來,把副檔名改成HTM,開啟看看,是不是特刺激?PREVIEW.DLG用到了幾個圖片檔案,在2110目錄下,別忘了匯出。(注:我的環境是Windows XP Professional英文版+SP1a,IE是6.0sp1。)
IE預設的模版中,頁首頁尾均只支援純文字。下面以定製HTML頁首為例,看看如何定製自己的列印模板。思路是:用自己的頁首內容換掉原有的內容,並修改其頁首高度和頁邊距使之和新的頁首相對應。
第一步,定義頁首。在要使用此模版預覽列印的HTML檔案中加入一個id為Header的div標記,括起HTML頁首內容,並制定以英寸為單位的頁首的高度和寬度,其中寬度應該和模版相符。例:
<div id="Header" style="DISPLAY:none; WIDTH:6.5in; HEIGHT:0.78in">
...(HTML頁首內容)
</div>
第二步,宣告變數。在模版前面變數宣告部分加上兩個變數宣告:
var g_htmlHeader = "";//用於儲存頁首內容
var g_nHeaerHeight = 0;//頁首的高度
第三步,取得頁首。在函式OnLoadBody()中的“Printer.footer = dialogArguments.__IE_FooterString”語句之後加入這段程式碼:
oPageHeader = dialogArguments.__IE_BrowseDocument.all.item("Header", 0);
if (null != oPageHeader)
{
g_htmlHeader = oPageHeader.innerHTML;
g_nHeaerHeight = oPageHeader.style.posHeight;
}
第四步,指定頁邊距和頁首高度。在上面的程式碼下面緊接著加入:
//指定頁邊距。其中40可以自己改,單位是百分之一英寸。
Printer.marginTop = 40 + (g_nHeaerHeight * 100);
Printer.marginBottom = 40;
Printer.marginLeft = 40;
Printer.marginRight = 40;
在函式EnsureDocuments()中,
/*註釋掉以下程式碼
if (header)
{
tmp = upTop + (27 / 100);
if (tmp > top)
top = tmp;
}
if (footer)
{
tmp = upBottom + (27 / 100);
if (tmp > bottom)
bottom = tmp;
}
*/
//緊接著加上:
tmp = upTop + g_nHeaerHeight;
if (tmp > top)
top = tmp;
//下面隔幾行,註釋掉:oRule.style.top = upTop + "in";
第五步,指定頁首內容。在函式CPrintDoc_AddPage()中,在“HeadFoot.page = HeadFoot.pageTotal;”語句之後加入:
//這兩行用於設定頁碼,你在頁首可以通過加入“[P]”和“[p]”分別代表總頁數和當前頁數。
g_htmlHeader = g_htmlHeader.replace("[P]", "<span class='hfPageTotal'></span>");
var pageHeader = g_htmlHeader.replace("[p]", HeadFoot.pageTotal);
//下面隔3行,註釋掉:
//~oPage.children("header").innerHTML = HeadFoot.HtmlHead;
//~oPage.children("footer").innerHTML = HeadFoot.HtmlFoot;
//下面隔幾行,把“newHTM += HeadFoot.HtmlHead;”改為:
newHTM += pageHeader ;
//然後註釋掉(不要頁尾):newHTM += HeadFoot.HtmlFoot;
至此,一個支援自定義HTML頁首的新模版就定製完成了。是不是覺得特爽?如果覺得它給你省下了兩週的時間,就趕緊到“希望之光”網站上,花你2天的工資,資助一個小孩上學吧。
定製好的列印模板怎麼用呢?請看以下程式碼:
var
vaIn, vaOut: OleVariant;
CmdTarget : IOleCommandTarget;
MyHandle : THandle;
begin
vaIn := 'c:\\Preview.htm';
//預覽方法1:WebBrowser1.ControlInterface.ExecWB(OLECMDID_PRINTPREVIEW, OLECMDEXECOPT_PROMPTUSER, vaIn, vaOut);
//下面是方法2:
if WebBrowser1.Document <> nil then
begin
WebBrowser1.Document.QueryInterface(IOleCommandTarget, CmdTarget);
if CmdTarget <> nil then
begin
try
CmdTarget.Exec( PGuid(nil), OLECMDID_PRINTPREVIEW, OLECMDEXECOPT_PROMPTUSER, vaIn, vaOut);
finally
CmdTarget._Release;
end;
end
else
begin
ShowMessage('IE不支援該功能,請升級至IE5.5以上。');
end;
end;
end;
方法1簡潔,但是如果WebBroswer不支援列印預覽的話就會出錯。第二種方法可能更好一些。
在列印預覽時,預覽視窗的尺寸大小總是和WebBrowser所在的Form的一樣,而且沒法最大化。更麻煩的是,如果你的表單是嵌入到了別的表單的元件上顯示的,例如,Form1.Parent := Form2.Panel1,即Form1顯示在Form2中Panel1所佔據的位置,那麼預覽視窗就變得很小了,不拉大根本沒法看。解決辦法如下,在預覽的程式碼後面加上以下程式碼,使預覽視窗最大化:
Handle:=FindWindow('Internet Explorer_TridentDlgFrame', '列印預覽');
if 0 <> MyHandle then
begin
ShowWindow(MyHandle , SW_MAXIMIZE);
end;
如果不預覽而是直接列印,則把OLECMDID_PRINTPREVIEW換成OLECMDID_PRINT就可以了。
如果要在Web應用中使用列印模板,可以通過ActiveX來實現呼叫。
注:列印模板需要安裝Internet Explorer 5.5以上版本,本文其它功能需要安裝Internet Explorer4.0以上版本。
如何打包HTML和相關檔案
應用做好了,總不能把HTML檔案和相關的圖片檔案等直接釋出吧。這樣既不安全,前面禁止使用者檢視原始碼的努力也白費了。因此至少應該將這些檔案打個包。一般來說,作為資源編譯到exe或dll裡就行了。我覺得編譯到DLL中最為方便。在Visual Studio中,新建一個Win32工程,應用型別選擇DLL。然後把HTML檔案和相關的圖片檔案等資源加到工程中,然後編譯即可。
再新增HTML等資源時,我強烈推薦用手工加入的方法。原因有二:一,GIF等圖片檔案加入到工程中時,Studio可能會把檔案內容自動改了,使得該檔案不能正確顯示;二,加入資源後會自動生成資源ID,需要把它改成你需要的名稱(通常改成和檔名相同),當檔案很多時,這項工作就很浪費時間,也很煩人。手工加入,即用文字編輯器把資源指令碼檔案(工程名.rc)開啟,手工加入內容。我就不贅述了,格式例子如下:
About.htm HTML "HTML\\About.htm"
image016.gif IMAGES "HTML\\images\\image016.gif"
當加入很多檔案時,如何節省時間呢?沒有實踐經驗的人,是不可能想到這些問題的。彆著急,按我說的做。
首先,進入命令列(DOS)介面(Windows NT/2000/XP/2003下執行cmd.exe進入),進入你的HTML等資原始檔所在的目錄,執行“dir > temp.txt”,把檔案列表輸出到temp.txt。
接著,用文字編輯器把該檔案開啟,去掉頭尾內容,僅留檔案列表部分,例如:
2004-03-17 11:20 20,397 About.htm
2004-03-17 11:20 27,397 index.htm
然後,用Excel把修改後的檔案開啟。開啟時,“原始資料型別”請選擇“固定寬度 - 每列欄位加空格對齊”。這樣,日期、時間、檔案大小、檔名就被分別放在了不同的列中。刪除前三列,僅留檔名一列,並把該列複製一份。在兩個檔名列之間插入兩個空列,分別填寫“HTML”和“"HTML\\”,然後就可以另存成以製表符分隔的文字檔案了。
最後,用文字編輯器把上一步處理好的檔案開啟,不用我多說,只要幾個替換,就得到所需要的資源指令碼了。對於不同目錄下的檔案,均需要這麼弄以下。
資源指令碼弄好了,把資原始檔也加入(不是作為資源加入)工程,編譯,就得到打包好的DLL檔案了。
接下來的問題是,這個DLL怎麼用啊?別急,WebBrowser支援一種叫res的協議,可以訪問檔案裡的資源。
例如,假設上面About.htm打包到了myresource.dll檔案中,則可以通過res://myresource.dll/About.htm訪問,image016.gif則可通過res://myresource.dll/images/image016.gif訪問(注意到了吧,HTML在根目錄下,而IMAGES等其它資源則在同名目錄下)。
如果About.htm中通過“images/image016.gif”引用了image016.gif檔案,則該圖片在WebBrowser中正常顯示。換句話說,你在打包之前,程式可以通過file://...訪問HTML,打包之後,只需要換成res://...就可以了----打包對程式和HTML幾乎沒什麼影響。但是,切記,切記!千萬不要僅以數字來做檔名(如:1.htm、2.gif等),因為數字是被用來標識某種資源或某個資源的,如果用僅用數字作檔名(可以用字母+數字),打包後會導致訪問找不到檔案。
相關文章
- WPF和js互動 WebBrowser資料互動JSWeb
- 06_應用層和核心層實現資料互動
- 雷達互動投影在應用中表現出的特點介紹
- MRCP在美團語音互動中的實踐和應用
- Flask 運用Xterm實現互動終端Flask
- 11個移動應用開發的HTML5框架和應用HTML框架
- 棧的應用和實現
- WebBrowser頁面與WinForm互動技巧WebORM
- HTML程式碼混淆技術:原理、應用和實現方法詳解HTML
- 用ISAPI方式實現Web頁面的自動更新 (轉)APIWeb
- 炫酷實用的HTML5應用和jQuery外掛HTMLjQuery
- js和原生應用常用的資料互動方式JS
- Servlet實現、與html的簡單互動ServletHTML
- AR真人大屏互動的應用形式和特點
- 實時語音互動,打造更加智慧便捷的應用
- HTML5 應用程式快取簡介HTML快取
- 基於HTML5實現3D監控應用流動效果HTML3D
- 一文搞懂SaaS應用架構:應用服務、應用結構、應用互動設計應用架構
- 三分鐘瞭解互動滑軌屏的實現原理與應用
- 用“雲”實現SaaS應用
- USB3.0介面的工業相機應用
- 10款很酷的HTML5動畫和實用應用 有原始碼HTML動畫原始碼
- 如何應用多媒體互動展廳的互動技術
- 《iOS應用開發指南——使用HTML5、CSS3和JavaScript》——1.3 移動應用程式≠桌面應用程式iOSHTMLCSSS3JavaScript
- 動態原型和互動式應用程式建立軟體:Principle中文漢化原型
- less的介紹和應用
- HTML5 殺不死移動應用HTML
- 攻擊JavaWeb應用[2]-CS互動安全JavaWeb
- 體感互動的多方面應用
- 攻擊JavaWeb應用————2、CS互動安全JavaWeb
- 口袋中的應用:互動介面重思考
- 《iOS應用開發指南——使用HTML5、CSS3和JavaScript》——1.3節移動應用程式≠桌面應用程式iOSHTMLCSSS3JavaScript
- 【expect】用expect實現scp/ssh-copy-id的非互動
- 移動應用營銷分析:消費者如何發現和使用應用
- 期待和現實:營銷自動化應用狀況報告
- Eclipse/tomcat 如何實現應用熱部署和熱啟動EclipseTomcat熱部署
- C#中介面的顯式實現與隱式實現及其相關應用案例C#
- Redux與前端表格施展“組合拳”,實現大屏展示應用的互動增強Redux前端