熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Delphi編程 >> 正文

利用浏覽器實現程序界面與實現的分離

2022-06-13   來源: Delphi編程 
 引言
  在用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完全一樣
  
   總結
  從上面的例子可以看到我們以及其簡單的方式實現了程序界面與實現的分離這有利於程序的維護和擴展傳統方式下界面設計和編碼通常都由程序員來完成一來造成程序員負擔較重二來難以保證界面質量實用上述方法程序界面可以由專業美工人員來設計他可以在完全不知道程序如何實現的情況下設計出完整的界面而程序員只需專注於代碼的編寫並將必要的方法和屬性通過一個自動化接口暴露出來合並的時候在網頁中合適的位置放入所需的按鈕或其它網頁元素並賦予簡單的腳本調用即可
From:http://tw.wingwit.com/Article/program/Delphi/201311/8412.html
    推薦文章
    Copyright © 2005-2022 電腦知識網 Computer Knowledge   All rights reserved.