引言
在用Delphi
Visual Basic等可視化快速開發工具編寫Windows應用程序時
常會遇到這樣幾個問題
) 希望程序界面美觀
在Delphi中
開發人員通常使用各種控件來實現界面的風格化
但缺點是造成應用程序體積較大
且在升級時常會被控件版本與Delphi版本不兼容帶來的問題所困擾
) 希望應用程序在功能不變的情況下具有不同的界面風格
這常常通過換
皮膚
的技術來實現
但一般實現
換膚
功能的控件體積都較大
且界面反應速度比較慢
而且
皮膚
的制作比較麻煩
) 程序界面的維護困難
為了使界面與代碼實現相分離而獲得
換膚
等靈活性
通常要用到一些設計模式的技術
這對於不熟悉設計模式的開發人員來說比較困難
微軟公司預計將於
年發布下一代操作系統(開發代號為Longhorn)中
應用程序的結構及部署將有重大變革
其中一項就是應用程序的界面完全以XML的一個擴展集XAML語言來描述
以便達到界面的高度可定制性
這無疑能夠方便地解決上述幾個問題
問題是在目前來說有沒有類似的方法呢?答案就是使用浏覽器控件
微軟公司的網頁浏覽器Internet Explorer的核心被設計為可以嵌入到應用程序中重用的ActiveX組件
它有極強的可編程能力和與容器交互的能力
使得開發人員能夠快速地開發出功能強勁的應用程序
從下面的Internet Explorer的架構圖可以看到
我們平常運行的iexplorer
exe其實只是一個外殼程序
真正的浏覽網頁
記錄歷史等工作是由嵌入其窗口的封裝在shdocvw
dll中的WebBrowser Control來完成的
Shdocvw
dll的功能則是調用mshtml
dll來解析網頁
以及在它的窗口中嵌入其它活動文檔組件(如Microsoft Office
Adobe Acrobat等應用程序的文檔都可以嵌入到浏覽器窗口中查看)
而mshtml
dll一方面處理HTML解析以及作為腳本引擎
java虛擬機
ActiveX控件
插件的宿主
另一方面
它實現了活動文檔服務器接口
允許應用程序以標准的COM接口來把它嵌入到程序中並通過它暴露的接口來訪問其中的網頁及網頁元素
通過shdocvw
dll提供的豐富接口
網頁中的元素可以訪問外殼應用程序提供的屬性和方法(如window
external
AddFavorite(location
href
document
title)則是調用IE的AddFavorite方法把當前頁添加到收藏夾)
而通過mshtml
dll提供的接口
外殼應用程序則反過來可以訪問網頁中元素的屬性
方法
行為
事件等等
解決文章開頭提出的幾個問題的方法就是基於shdocvw
dll和mshtml
dll實現的
一些著名軟件如
Microsoft Money
Microsoft Visual Studio
NET
Macromedia Dreamweaver MX
等都運用了這種技術
原理
) 程序的界面完全由制作網頁來完成
網頁在文字
圖像
聲音等方面具有強大的表現能力
運用所見即所得的網頁制作工具可以輕松制作出圖文並茂的網頁
以網頁作為程序的界面
其效果勝過任何界面控件
)
換膚
功能容易實現
只需制作不同風格的網頁
即可輕松實現樣式各異的程序界面
) 程序的功能在應用程序內部編寫代碼來實現
並通過一個自動化接口提供給網頁中的元素調用
這就實現了程序界面和代碼的分離
網頁布局及風格的改變不會影響到程序的實現
從網頁調用外殼程序的屬性和方法
GetExternal接口方法
WebBrowser Control提供的接口使得外殼應用程序可以用自己的對象
方法和屬性等來擴展IE的對象模型(DOM)
以達到個性化定制的目的
在網頁中訪問外殼應用程序的擴展則通過文檔的
external
對象來實現
如外殼程序提供了名為AddFavorite的方法
網頁中就通過window
external
AddFavorite()來調用
實現這一功能的核心是IDocHostUIHandler接口的GetExternal方法
HRESULT GetExternal(IDispatch **ppDispatch);
在自定義的WebBrowser Control中實現IDocHostUIHandler接口
當網頁元素通過
external
對象訪問外殼擴展的屬性和方法時
GetExternal方法就會被調用
在此方法的中將實現外殼程序屬性和方法的自動化接口傳遞給ppDispatch即可
自定義的WebBrowser Control示例代碼如下
在其中將GetExternal包裝為OnGetExternal事件供外部程序調用
IDocHostUIHandler接口有
個方法
此處我們只關心GetExternal方法
故略去其余
個(省略號處為略去的代碼)
unit ZoCWebBrowser;
interface
uses
Variants
IEConst
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 TZoCWebBrowser
GetExternal(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
一欄中填入接口名
MyExternal
Instancing
選擇為
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 TMyExternal
ShowAboutBox;
begin
MessageBox(MainForm
Handle
GetExternal Demo
ZoCWebBrowser
MB_OK or MB_ICONASTERISK);
end;
procedure TMyExternal
SwitchUI;
begin
ShowSwitchUIForm; //顯示切換程序界面對話框
end;
initialization
TAutoObjectFactory
Create(ComServer
TMyExternal
Class_MyExternal
ciInternal
tmApartment);
end
從網頁中調用外殼程序接口
在程序主窗口中放置一個自定義的WebBrowser Control
命名為ZoCWebBrowser
編寫它的OnGetExternal事件(由網頁中的window
external調用觸發)
代碼如下
function TMainForm
ZoCWebBrowserGetExternal(
out ppDispatch: IDispatch): HRESULT;
var
MyExternal: TMyExternal;
begin
MyExternal:= TMyExternal
Create; //創建實現自動化接口的對象
ppDispatch :=MyExternal; //將對象接口傳遞給WebBrowser Control
//這樣當
external
對象被調用時
真正被調用的是我們實現的TMyExternal對象
Result :=S_OK;
end;
假設我們制作了兩個風格迥異的的網頁l和l作為程序界面
這兩個網頁中都有兩個按鈕(也可以是其它網頁元素)
其HTML代碼示例如下
<BUTTON window
external
ShowAboutBox
>關於</BUTTON>
<BUTTON window
external
SwitchUI
>切換界面</BUTTON>
在程序開始運行時讓WebBrowser Control布滿整個Form
且顯示l頁面
則當點擊
關於
按鈕時程序將顯示一個關於信息對話框
而點擊
切換界面
按鈕時將顯示切換界面的對話框
在其中選擇l並讓WebBrowser Control顯示它即可獲得風格完全不同的界面
但在功能上與l完全一樣
總結
從上面的例子可以看到
我們以及其簡單的方式實現了程序界面與實現的分離
這有利於程序的維護和擴展
傳統方式下
界面設計和編碼通常都由程序員來完成
一來造成程序員負擔較重
二來難以保證界面質量
實用上述方法
程序界面可以由專業美工人員來設計
他可以在完全不知道程序如何實現的情況下設計出完整的界面
而程序員只需專注於代碼的編寫
並將必要的方法和屬性通過一個自動化接口暴露出來
合並的時候
在網頁中合適的位置放入所需的按鈕或其它網頁元素
並賦予簡單的腳本調用即可
From:http://tw.wingwit.com/Article/program/Delphi/201311/8412.html