电脑故障

位置:IT落伍者 >> 电脑故障 >> 浏览文章

利用浏览器实现程序界面与实现的分离


发布日期:2023/10/1
 
引言

在用DelphiVisual Basic等可视化快速开发工具编写Windows应用程序时常会遇到这样几个问题

) 希望程序界面美观在Delphi中开发人员通常使用各种控件来实现界面的风格化但缺点是造成应用程序体积较大且在升级时常会被控件版本与Delphi版本不兼容带来的问题所困扰

) 希望应用程序在功能不变的情况下具有不同的界面风格这常常通过换皮肤的技术来实现但一般实现换肤功能的控件体积都较大且界面反应速度比较慢而且 皮肤的制作比较麻烦

) 程序界面的维护困难为了使界面与代码实现相分离而获得换肤等灵活性通常要用到一些设计模式的技术这对于不熟悉设计模式的开发人员来说比较困难

微软公司预计将于年发布下一代操作系统(开发代号为Longhorn)中应用程序的结构及部署将有重大变革其中一项就是应用程序的界面完全以XML的一个扩展集XAML语言来描述以便达到界面的高度可定制性这无疑能够方便地解决上述几个问题问题是在目前来说有没有类似的方法呢?答案就是使用浏览器控件

微软公司的网页浏览器Internet Explorer的核心被设计为可以嵌入到应用程序中重用的ActiveX组件它有极强的可编程能力和与容器交互的能力使得开发人员能够快速地开发出功能强劲的应用程序从下面的Internet Explorer的架构图可以看到我们平常运行的iexplorerexe其实只是一个外壳程序真正的浏览网页记录历史等工作是由嵌入其窗口的封装在shdocvwdll中的WebBrowser Control来完成的

Shdocvwdll的功能则是调用mshtmldll来解析网页以及在它的窗口中嵌入其它活动文档组件(如Microsoft OfficeAdobe Acrobat等应用程序的文档都可以嵌入到浏览器窗口中查看)而mshtmldll一方面处理HTML解析以及作为脚本引擎java虚拟机ActiveX控件插件的宿主另一方面它实现了活动文档服务器接口允许应用程序以标准的COM接口来把它嵌入到程序中并通过它暴露的接口来访问其中的网页及网页元素

通过shdocvwdll提供的丰富接口网页中的元素可以访问外壳应用程序提供的属性和方法(如windowexternalAddFavorite(locationhref documenttitle)则是调用IE的AddFavorite方法把当前页添加到收藏夹)而通过mshtmldll提供的接口外壳应用程序则反过来可以访问网页中元素的属性方法行为事件等等解决文章开头提出的几个问题的方法就是基于shdocvwdll和mshtmldll实现的一些着名软件如Microsoft MoneyMicrosoft Visual Studio NETMacromedia Dreamweaver MX 等都运用了这种技术

原理

) 程序的界面完全由制作网页来完成网页在文字图像声音等方面具有强大的表现能力运用所见即所得的网页制作工具可以轻松制作出图文并茂的网页以网页作为程序的界面其效果胜过任何界面控件

) 换肤功能容易实现只需制作不同风格的网页即可轻松实现样式各异的程序界面

) 程序的功能在应用程序内部编写代码来实现并通过一个自动化接口提供给网页中的元素调用这就实现了程序界面和代码的分离网页布局及风格的改变不会影响到程序的实现

从网页调用外壳程序的属性和方法

GetExternal接口方法

WebBrowser Control提供的接口使得外壳应用程序可以用自己的对象方法和属性等来扩展IE的对象模型(DOM)以达到个性化定制的目的在网页中访问外壳应用程序的扩展则通过文档的external对象来实现如外壳程序提供了名为AddFavorite的方法网页中就通过windowexternalAddFavorite()来调用实现这一功能的核心是IDocHostUIHandler接口的GetExternal方法

HRESULT GetExternal(IDispatch **ppDispatch);

在自定义的WebBrowser Control中实现IDocHostUIHandler接口当网页元素通过external对象访问外壳扩展的属性和方法时GetExternal方法就会被调用在此方法的中将实现外壳程序属性和方法的自动化接口传递给ppDispatch即可自定义的WebBrowser Control示例代码如下在其中将GetExternal包装为OnGetExternal事件供外部程序调用IDocHostUIHandler接口有个方法此处我们只关心GetExternal方法故略去其余个(省略号处为略去的代码)

unit ZoCWebBrowser;

interface

uses

VariantsIEConst Windows SysUtils Classes SHDocVw ActiveX shlObj MSHTML comobj;

type

……

TGetExternalEvent = function(out ppDispatch: IDispatch): HRESULT of object;//定义OnGetExternal事件类型

TZoCWebBrowser = class(TWebBrowser IDocHostUIHandler)

private

……

FOnGetExternal: TGetExternalEvent;

protected

……

function GetExternal(out ppDispatch: IDispatch): HRESULT; stdcall;

published

……

property OnGetExternal: TGetExternalEvent read FOnGetExternal write FOnGetExternal;

end;

……

implementation

……

function TZoCWebBrowserGetExternal(out ppDispatch: IDispatch): HRESULT;

begin

if Assigned(FOnGetExternal) then

Result := FOnGetExternal(ppDispatch)

else

Result := S_FALSE;

end;

initialization

OleInitialize(nil);

finalization

try

OleUninitialize;

except

end;

end

实现外壳程序扩展自动化接口

在Delphi的New Items对话框中切换到ActiveX选择Automation Object新建一个自动化对象并在CoClass Name一栏中填入接口名MyExternalInstancing选择为Internal表示该对象只能在程序内部被创建外部程序不能直接创建点击OK按钮后在Type Library编辑对话框中为IMyExternal接口添加两个方法ShowAboutBox和SwitchUI此时代码大致如下所示

unit MyExternalImpl;

{$WARN SYMBOL_PLATFORM OFF}

interface

uses

ComObj ActiveX Project_TLB StdVcl;

type

TMyExternal = class(TAutoObject IMyExternal)

protected

procedure ShowAboutBox; safecall;

procedure SwitchUI; safecall;

end;

implementation

uses ComServ;

procedure TMyExternalShowAboutBox;

begin

MessageBox(MainFormHandle GetExternal Demo ZoCWebBrowser MB_OK or MB_ICONASTERISK);

end;

procedure TMyExternalSwitchUI;

begin

ShowSwitchUIForm; //显示切换程序界面对话框

end;

initialization

TAutoObjectFactoryCreate(ComServer TMyExternal

Class_MyExternal ciInternal tmApartment);

end

从网页中调用外壳程序接口

在程序主窗口中放置一个自定义的WebBrowser Control命名为ZoCWebBrowser编写它的OnGetExternal事件(由网页中的windowexternal调用触发)代码如下

function TMainFormZoCWebBrowserGetExternal(

out ppDispatch: IDispatch): HRESULT;

var

MyExternal: TMyExternal;

begin

MyExternal:= TMyExternalCreate; //创建实现自动化接口的对象

ppDispatch :=MyExternal; //将对象接口传递给WebBrowser Control

//这样当external对象被调用时真正被调用的是我们实现的TMyExternal对象

Result :=S_OK;

end;

假设我们制作了两个风格迥异的的网页l和l作为程序界面这两个网页中都有两个按钮(也可以是其它网页元素)其HTML代码示例如下

<BUTTON windowexternalShowAboutBox>关于</BUTTON>

<BUTTON windowexternalSwitchUI>切换界面</BUTTON>

在程序开始运行时让WebBrowser Control布满整个Form且显示l页面则当点击关于按钮时程序将显示一个关于信息对话框而点击切换界面按钮时将显示切换界面的对话框在其中选择l并让WebBrowser Control显示它即可获得风格完全不同的界面但在功能上与l完全一样

总结

从上面的例子可以看到我们以及其简单的方式实现了程序界面与实现的分离这有利于程序的维护和扩展传统方式下界面设计和编码通常都由程序员来完成一来造成程序员负担较重二来难以保证界面质量实用上述方法程序界面可以由专业美工人员来设计他可以在完全不知道程序如何实现的情况下设计出完整的界面而程序员只需专注于代码的编写并将必要的方法和属性通过一个自动化接口暴露出来合并的时候在网页中合适的位置放入所需的按钮或其它网页元素并赋予简单的脚本调用即可

上一篇:使用剪切板[3]:SetComponent、GetComponent

下一篇:使用剪切板[4]:如果把子控件一起复制?