Delphi实现HTMLWebBrowser实现HTML界面
HTML的界面有以下特点:图文混排,格式灵活,可以包含Flash、声音和视频等,实现图文声像的多媒体界面,而且易于建立和维护。另外,HTML的显示环境一般机器上都具备,通常不需要安装额外的软件。当然,HTML界面也有它欠缺的方面,即:界面控制能力有限,代码调试不便----虽然DHTML提供了比较强的编程特性,但是比起Delphi的传统的开发语言和工具来,对界面的控制能力,尤其是和数据交互时的控制能力还是稍逊一筹。
了解了这些特点,我们就可以在实际应用开发中,适时地选择HTML技术。下面举个例子:
一种仪器的管理程序,需要显示该仪器的操作方法文档,包含文字和图片,并要求可以隐藏或显示文档,并能安要求打印。
这个应用中,图文显示、隐藏/显示部分文档、图文打印等需求,都是HTML界面所擅长的,用传统的表单控件实现几乎无法想像。
用什么实现HTML的界面
用Delphi实现HTML界面的应用主要有两种选择:WebBrowser Control或MSHTML。为了弄清两者如何选择,我们先来看看Internet Exporer 4.0及其后续版本的体系结构:
http://msdn.microsoft.com/workshop/graphics/IE4Arch.gif" width=305 border=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'>http://msdn.microsoft.com/downloads/samples/internet/browser/walkall/default.asp">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元件代码示例:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
procedure xx();
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 begin 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中的调用代码如下:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 |
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'>http://members.shaw.ca/iedelphi/downloads/source/ieConst.pas">ieConst.pas 和 IEDocHostUIHandler.pas'>http://members.shaw.ca/iedelphi/downloads/source/IEDocHostUIHandler.pas">IEDocHostUIHandler.pas。使用方法如下:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
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类(该源码请看本文的附件),实现就简单多了。实现代码如下:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
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表单上输入,因为一输入就触发相应按钮的单击事件。解决代码如下:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
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 |
本段代码出自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 (下载:http://download.microsoft.com/download/6/8/3/683DB9FE-8D61-4A3C-B7B8-3169FF70AE9F/printtemplates.exe">打印模板示例)。
你会发现,要自己实现一个功能完善的打印模板也并非易事。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等),因为数字是被用来标识某种资源或某个资源的,如果用仅用数字作文件名(可以用字母+数字),打包后会导致访问找不到文件。
参考:http://m.blog.csdn.net/blog/fghydx/18548257
Delphi实现HTMLWebBrowser实现HTML界面的更多相关文章
- Delphi 安装Cnpack cnvcl后界面不断弹出 Memory Manager's list capablity overflow ,please enlarge it!
Delphi 安装Cnpack cnvcl后界面不断弹出 Memory Manager's list capablity overflow ,please enlarge it! 解决方法:删除指定 ...
- delphi 一个线程和主界面的交互的演示代码
求一个线程和主界面的交互的演示代码求一个线程和主界面的交互的演示代码.线程和主界面处于两个Unit.线程中的user中不能引用主窗口.我只是想学习一下,线程和主界面交互的方法.去网上查了好几天资料,能 ...
- Delphi : 制作程序启动欢迎界面
制作欢迎界面的代码,大家参考 登陆窗体命名為:loginform.找到工程文件(Project -> View Source),找到如下代碼部分:begin Application.Init ...
- Delphi XE10让android的界面设计摆脱繁杂
设计一个选项卡. 大体图样: 1.创建一个multi_Device_Application;2.在form上放一个Rectangle1,设置align为top.设置fill属性的kind为Gradie ...
- Delphi 记事本 TMemo
Windows记事本记事本 描述: 用Delphi模仿的Windows记事本 界面和功能都和Windows的记事本一样,是用Memo实现的而不是RichEdit 可以执行以下功能 文件 ...
- 玄虚出品Delphi教程的前言 good
VCL是基于系统的,根本在于系统API,FMX是基于绘图的,根本在于渲染引擎 VCL的发展受制于系统,(你在VCL的代码里面可以看到Borland对M$的妥协),而FMX的发展仅仅受制于硬件 VCL ...
- 发现了一个很好的Pasical编程软件Lazarus
下载地址:http://www.lazarus.freepascal.org/ 中文社区:http://www.fpccn.com/ 该软件有几点如下特征: 1.跨平台.体积小(只有100多M) 2. ...
- 简单详细的OD破解教程
2007-08-04 15:46作者:CCDebuger注:昨天在网上见到了这篇文章,但缺少插图,从另外一篇文章中也看到了类似的的教程文章,里面的插图质量实在不敢恭维.在一个论坛中正好下载了文章中所介 ...
- OllyDBG 破解入门教程
一.OllyDBG 的安装与配置OllyDBG 版的发布版本是个 ZIP 压缩包,只要解压到一个目录下,运行 OllyDBG.exe 就可以了.汉化版的发布版本是个 RAR 压缩包,同样只需解压到一 ...
随机推荐
- Java Interface是存放常量的最好地方吗?(转)
虽然Inteface中默认常量就是static final 的.因此很多人就认为其实存放常量的最佳地方,effective java中就提过,不建议使用常量接口.其有一个原因就是:代码编译问题 好,我 ...
- JavaScript 应用开发 #2:视图与模板
在用 Backbone 开发的 JavaScript 应用里面,除了模型与集合以外,另一个重要的部分就是视图,英文是 View .在视图里面,我们可以去监听在页面上发生的事件,还有与视图相关的模型和集 ...
- Chapter 5. The Gradle Wrapper 关于gradle wrapper
Most tools require installation on your computer before you can use them. If the installation is eas ...
- 配置toad进行导入导出数据库操作
最近需要对oracle进行导入导出操作,于是研究使用toad进行导入导出oracle数据库操作,现稍作记录. oracle的导入导出不管是否使用toad还是pl/sql工具进行操作,实际上最终都是通过 ...
- Linq扩展方法之Aggregate 对序列应用累加器函数
Linq扩展方法之Aggregate 对序列应用累加器函数; 函数模板:// 函数名:对序列应用累加器函数. // Parameters:参数要求 // source:要聚合的 System.Col ...
- html通用导航条制作
第一步:先创建一个盒子,定义类为 nav,width 1000,height 40px,防京东的导航,与浏览器顶部100px,margin-top:100px,看的更直观 第二步:使用无序列表放置,导 ...
- 在VS中关于MySQL的相关问题
最近在vs上折腾mysql数据库 遇到了一些小问题,这里记录一下 问题一:数据源选择中没有mysql数据库的选项 解放方法: 1.安装MySql的VS插件(版本请下载最新版)mysql-for-vis ...
- asp.net mvc5 设置Area下的为启动页
只需修改App_Start文件夹下RouteConfig中RegisterRoutes方法 public static void RegisterRoutes(RouteCollection rout ...
- iOS Core Animation学习总结(1)--CALayer常用属性
图层是core animation的基础, UIView之所以能显示在屏幕上,靠的是其内部的这个图层,即每个UIView 都有 CALayer,可通过UIView.layer或者[UIView lay ...
- Java RMI 框架_远程方法调用(2016-08-16)
概念: Java RMI 指的是远程方法调用 (Remote Method Invocation).它是一种机制,能够让在某个 Java 虚拟机上的对象调用另一个 Java 虚拟机中的对象上的方法.可 ...