HTML的界面有如下特色:圖文混排,格式靈活,能夠包含Flash、聲音和視頻等,實現圖文聲像的多媒體界面,並且易於創建和維 護。另外,HTML的顯示環境通常機器上都具有,一般不須要安裝額外的軟件。固然,HTML界面也有它欠缺的方面,即:界面控制能力有限,代碼調試不便 ----雖然DHTML提供了比較強的編程特性,可是比起Delphi的傳統的開發語言和工具來,對界面的控制能力,尤爲是和數據交互時的控制能力仍是稍 遜一籌。html
瞭解了這些特色,咱們就能夠在實際應用開發中,適時地選擇HTML技術。下面舉個例子:web
一種儀器的管理程序,須要顯示該儀器的操做方法文檔,包含文字和圖片,並要求能夠隱藏或顯示文檔,並能安要求打印。數據庫
這個應用中,圖文顯示、隱藏/顯示部分文檔、圖文打印等需求,都是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,點擊連接將致使在新的瀏覽器實例中打開連接的文檔。)編輯器
首先,在Delphi 7.0組件面板的Internet頁上,把TWebBrowser組件放到表單上,並手動把MSHTML加入到Uses列表中。ide
經過執行如下語句裝載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。
知道了訪問HTML內容的方法,就能夠經過間接方式調用HTML頁面上包含的JavaScript代碼。具體實現方式是:在HTML中插 入<span></span>等不可見元件,利用它的click事件調用響應的JavaScript函數,而後再Delphi中 調用該元件的click過程。
下面咱們就用Delphi調用JavaScript的alert函數來實現消息提示框。首先在HTML中加入:
<span style="display:none" ></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事件的響應方式有兩種:一種是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過程。
含有多行文本輸入框(textarea )或提交(submit)按鈕的HTML表單在TWebBrowser中顯示時,對回車鍵不響應。另外,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的打印和預覽向來是個難題,但自從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 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()中,
//緊接着加上:
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 ></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文件和相關的圖片文件等直接發佈吧。這樣既不安全,前面禁止用戶查看源代碼的努力也白費了。所以至少應該將這些文 件打個包。通常來講,做爲資源編譯到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等),由於數字是被用來標識某種資源或某個資源的,若是用僅用數字做文件名(能夠用字母+數字),打包後會致使訪問找不到文件。