delphi与网页交互

HTML的界面有如下特色:图文混排,格式灵活,能够包含Flash、声音和视频等,实现图文声像的多媒体界面,并且易于创建和维 护。另外,HTML的显示环境通常机器上都具有,一般不须要安装额外的软件。固然,HTML界面也有它欠缺的方面,即:界面控制能力有限,代码调试不便 ----虽然DHTML提供了比较强的编程特性,可是比起Delphi的传统的开发语言和工具来,对界面的控制能力,尤为是和数据交互时的控制能力仍是稍 逊一筹。html

了解了这些特色,咱们就能够在实际应用开发中,适时地选择HTML技术。下面举个例子:web

一种仪器的管理程序,须要显示该仪器的操做方法文档,包含文字和图片,并要求能够隐藏或显示文档,并能安要求打印。数据库

这个应用中,图文显示、隐藏/显示部分文档、图文打印等需求,都是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组件放到表单上,并手动把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。

 

如何调用JavaScript函数(兼谈消息提示框)

知道了访问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的事件(如何在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中显示时,对回车键不响应。另外,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 = nilthen 
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 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和相关文件

应用作好了,总不能把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等),由于数字是被用来标识某种资源或某个资源的,若是用仅用数字做文件名(能够用字母+数字),打包后会致使访问找不到文件。

相关文章
相关标签/搜索