FireMonkey Windows下的WebBrowser组件编写

来源:互联网 发布:裂脑人 知乎 编辑:程序博客网 时间:2024/05/21 06:25

其实FireMonkey非常好用, 但就是缺了WINDOWS下的浏览器组件,  有时程序可能需要一个嵌入式的框框, 而Delphi且没有为我们提供这个东东...咋整呢...

好像有大牛使用谷歌浏览器组件封装过一个唉, 具体不知道了,我也没看过, 听说还要带一大堆的dll文件, 貌似有几十M呢,.........好像用不着啊.........

那我们该怎么办呢....其实在FireMonkey的前身DxScene中有一个3d浏览器的例程, 不知道大家看过没有, 我是照着写了下, 不过是将浏览器封装在DLL中的,

这样可以必免引入vcl库了,,,,但不好就是要带一个dll走........实现了是实现了, 但感觉用着不咋爽,特别是大面积的还带有js自动刷新某些页面的时候就会发现

cpu占得很大, 绘图效果还不好了....


于是就有了一个想法, 能不能将浏览器组件冰封装在dll中的Form中呢.. 然后以子父窗口的形式来创建,,, 答案是肯定可以的,  接下来我们就开始实现吧


首先我们需要编写一个dll,, 在里面创建一个Form,再在 Form上放置一个浏览器组件, 将Form设置为无边框模式, Webbrowser设置为Client对齐

然后我们需要重新编写浏览器的事件, 如下

大家会发现少下sender这个参数, 其实我是故意不要的,因为也用不着, 呵呵, 这里我定义了浏览器最基本的一些事件, 然后该怎么办呢, 继续看吧

{***************************************************************************}{                                                                           }{       功能:FMX.ZYJ 浏览器组件                                            }{       名称:FMX.ZYJ.Win.WebBrowser.pas                                    }{       版本:1.1                                                           }{       环境:Win8.1                                                        }{       工具:Delphi XE3 AppMethod DelphiXE6                                }{       日期:2014/3/28 19:35:56                                            }{       作者:ying32                                                        }{       QQ  :1444386932                                                     }{       版权所有 (C) 2014-2014 ying32 All Rights Reserved                   }{                                                                           }{---------------------------------------------------------------------------}{                                                                           }{       备注: Windows浏览器组件                                            }{                                                                           }{                                                                           }{                                                                           }{***************************************************************************}

unit FMX.ZYJ.Win.WebBrowser;interfaceuses  Winapi.Windows,  WebBrowserUnit,  System.SysUtils,  System.Classes,  System.Types,  System.UITypes,  FMX.Types,  FMX.Controls,  FMX.Forms;

type   TWebDocumentComplete = procedure(const pDisp: IDispatch; var URL: OleVariant) of object;  TWebStatusTextChange = procedure(const Text: WideString) of object;  TWebTitleChange = procedure(const Text: WideString) of object;  TWebVisible = procedure(Visible: WordBool) of object;  TWebQuit = procedure of object;  TWebPropertyChange = procedure(const szProperty: WideString) of object;  TWebProgressChange = procedure(Progress, ProgressMax: Integer) of object;  TWebNewWindow2 = procedure(var ppDisp: IDispatch; var Cancel: WordBool) of object;  TWebNavigateComplete2 = procedure(const pDisp: IDispatch; var URL: OleVariant) of object;  TWebFullScreen = procedure(FullScreen: WordBool) of object;  TWebDownloadBegin = procedure of object;  TWebDownloadComplete = procedure of object;  TWebCommandStateChange = procedure(Command: Integer; Enable: WordBool) of object;  TWebBeforeNavigate2 = procedure(const pDisp: IDispatch; var URL, Flags,         TargetFrameName, PostData, Headers: OleVariant; var Cancel: WordBool) of object;


接下来我们需要定义一些私有的变量,将这个事件以属性的方式重新输出到外面去

  private    FOnDocumentComplete: TWebDocumentComplete;    FOnStatusTextChange: TWebStatusTextChange;    FOnTitleChange: TWebTitleChange;    FOnVisible: TWebVisible;    FOnQuit: TWebQuit;    FOnPropertyChange: TWebPropertyChange;    FOnProgressChange: TWebProgressChange;    FOnNewWindow2: TWebNewWindow2;    FOnNavigateComplete2: TWebNavigateComplete2;    FOnFullScreen: TWebFullScreen;    FOnDownloadBegin: TWebDownloadBegin;    FOnDownloadComplete: TWebDownloadComplete;    FOnCommandStateChange: TWebCommandStateChange;    FOnBeforeNavigate2: TWebBeforeNavigate2;  public    property OnDocumentComplete: TWebDocumentComplete read FOnDocumentComplete write FOnDocumentComplete;    property OnStatusTextChange: TWebStatusTextChange read FOnStatusTextChange write FOnStatusTextChange;    property OnTitleChange: TWebTitleChange read FOnTitleChange write FOnTitleChange;    property OnVisible: TWebVisible read FOnVisible write FOnVisible;    property OnQuit: TWebQuit read FOnQuit write FOnQuit;    property OnPropertyChange: TWebPropertyChange read FOnPropertyChange write FOnPropertyChange;    property OnProgressChange: TWebProgressChange read FOnProgressChange write FOnProgressChange;    property OnNewWindow2: TWebNewWindow2 read FOnNewWindow2 write FOnNewWindow2;    property OnNavigateComplete2: TWebNavigateComplete2 read FOnNavigateComplete2 write FOnNavigateComplete2;    property OnFullScreen: TWebFullScreen read FOnFullScreen write FOnFullScreen;    property OnDownloadBegin: TWebDownloadBegin read FOnDownloadBegin write FOnDownloadBegin;    property OnDownloadComplete: TWebDownloadComplete read FOnDownloadComplete write FOnDownloadComplete;    property OnCommandStateChange: TWebCommandStateChange read FOnCommandStateChange write FOnCommandStateChange;    property OnBeforeNavigate2: TWebBeforeNavigate2 read FOnBeforeNavigate2 write FOnBeforeNavigate2;  end;


好了, 我们已经建议好事件了, 接下来就是在浏览的事件中调用我们的这些事件,  这里给出一个参考, 其它都一样了 

procedure TWebForm.wbDocumentComplete(Sender: TObject;  const pDisp: IDispatch; var URL: OleVariant);begin  try    if Assigned(OnDocumentComplete) then      OnDocumentComplete(pDisp, URL);  except  end;end;


完成了以上我们的浏览器组件还不算呢, 接下来我们要导出这些东西,  在这里我是以标准的function导出的, 原来我打算创建一个类管理, 后来我虽然用了这个方法,但实际没多大用下, 大家将就着看吧

接下来我们编写创建浏览器的函数吧

function TWebWindowManager.BrowserCreate(AParent: Cardinal; AVisible: Boolean; ALeft, ATop, AWidth, AHeight: Integer): Cardinal;var   LWeb: TWebForm;begin   Result := 0;   if not IsWindow(AParent) then Exit;   LWeb := TWebForm.Create(Application);   if Assigned(LWeb) then   begin     Windows.SetParent(LWeb.Handle, AParent);     // 貌似有时无法前台,故有此函数调用     // Windows.SetForegroundWindow(AParent);     SetWindowPos(LWeb.Handle, 0, ALeft, ATop, AWidth, AHeight, SWP_NOACTIVATE);     if AVisible then       ShowWindow(LWeb.Handle, SW_SHOWNORAML);     FList.Add(LWeb);     Result := Cardinal(LWeb);//     Result := FList.Add(LWeb) + 1;   end;end;  

呵呵, 大家别理会注释掉的代码就好了. 那是以前写的方法....没去掉.....

释放掉

function TWebWindowManager.BrowserFree(Handle: Cardinal): Boolean;var  P: Pointer;begin  Result := False;  P := GetItem(Handle);  if Assigned(P) then  begin    TWebForm(P).Free;    FList.Remove(P);//    FList.Delete(Integer(Handle) - 1);    Result := True;  end;end;

再下来就是浏览器的几个方法了.

procedure TWebWindowManager.BrowserNavigateA(Handle: Cardinal; AURL: PAnsiChar);var  P: Pointer;begin  try    P := GetItem(Handle);    if Assigned(P) then      TWebForm(P).wb.Navigate(AURL);  except  end;end;procedure TWebWindowManager.BrowserNavigateW(Handle: Cardinal; AURL: PWideChar);var  P: Pointer;begin  try    P := GetItem(Handle);    if Assigned(P) then      TWebForm(P).wb.Navigate(AURL);  except  end;end;

实现方法很简单, 那事件呢? 其实一样也简单, 看下面

procedure TWebWindowManager.BrowserDocumentComplete(Handle: Cardinal;  AEvent: TWebDocumentComplete);var  P: Pointer;begin  P := GetItem(Handle);  if Assigned(P) then  begin    if (not Assigned(TWebForm(P).OnDocumentComplete)) or       (@TWebForm(P).OnDocumentComplete <> @AEvent) then     TWebForm(P).OnDocumentComplete := AEvent;  end;end;

是不是简单啊, 其实的都类似了, 这里就不贴出来了

然后我们导出这些函数, 实际上我外面还重新编写了一个stdcall的函数...因为当时没想好,又懒得改, 所以....

exports   BrowserCreate name 'webCreate',   BrowserFree name 'webFree',   BrowserSetVisible name 'webSetVisible',   BrowserMove name 'webMove',

呵呵,我重命名了....

以上dll部分就编写好了, 接下来是函数申明... .这个就不用我写了吧.....


再下来就是编写FMX组件啦
这里我再一次重新定义了事件

type  TWebDocumentCompleteEvent = procedure(Sender: TObject; const pDisp: IDispatch; var URL: OleVariant) of object;  TWebStatusTextChangeEvent = procedure(Sender: TObject; const Text: WideString) of object;  TWebTitleChangeEvent = procedure(Sender: TObject; const Text: WideString) of object;  TWebVisibleEvent = procedure(Sender: TObject; Visible: WordBool) of object;  TWebQuitEvent = procedure(Sender: TObject) of object;  TWebPropertyChangeEvent = procedure(Sender: TObject; const szProperty: WideString) of object;  TWebProgressChangeEvent = procedure(Sender: TObject; Progress, ProgressMax: Integer) of object;  TWebNewWindow2Event = procedure(Sender: TObject; var ppDisp: IDispatch; var Cancel: WordBool) of object;  TWebNavigateComplete2Event = procedure(Sender: TObject; const pDisp: IDispatch; var URL: OleVariant) of object;  TWebFullScreenEvent = procedure(Sender: TObject; FullScreen: WordBool) of object;  TWebDownloadBeginEvent = procedure(Sender: TObject) of object;  TWebDownloadCompleteEvent = procedure(Sender: TObject) of object;  TWebCommandStateChangeEvent = procedure(Sender: TObject; Command: Integer; Enable: WordBool) of object;  TWebBeforeNavigate2Event = procedure(Sender: TObject; const pDisp: IDispatch; var URL, Flags,         TargetFrameName, PostData, Headers: OleVariant; var Cancel: WordBool) of object;


下面是组件类啦

  TZYJWebBrowser = class(TControl)  strict private    FWebHandle: Cardinal;  private    FOnDocumentComplete: TWebDocumentCompleteEvent;    FOnStatusTextChange: TWebStatusTextChangeEvent;    FOnTitleChange: TWebTitleChangeEvent;    FOnVisible: TWebVisibleEvent;    FOnQuit: TWebQuitEvent;    FOnPropertyChange: TWebPropertyChangeEvent;    FOnProgressChange: TWebProgressChangeEvent;    FOnNewWindow2: TWebNewWindow2Event;    FOnNavigateComplete2: TWebNavigateComplete2Event;    FOnFullScreen: TWebFullScreenEvent;    FOnDownloadBegin: TWebDownloadBeginEvent;    FOnDownloadComplete: TWebDownloadCompleteEvent;    FOnCommandStateChange: TWebCommandStateChangeEvent;    FOnBeforeNavigate2: TWebBeforeNavigate2Event;    function GetDefaultInterface: IDispatch;    function GetDocument: IDispatch;  protected    procedure Resize; override;    procedure Paint; override;    procedure SetVisible(const Value: Boolean); override;    procedure FDocumentComplete(const pDisp: IDispatch; var URL: OleVariant);    procedure FStatusTextChange(const Text: WideString);    procedure FTitleChange(const Text: WideString);    procedure FVisible2(Visible: WordBool);    procedure FQuit();    procedure FPropertyChange(const szProperty: WideString);    procedure FProgressChange(Progress, ProgressMax: Integer);    procedure FNewWindow2(var ppDisp: IDispatch; var Cancel: WordBool);    procedure FNavigateComplete2(const pDisp: IDispatch; var URL: OleVariant);    procedure FFullScreen(FullScreen: WordBool);    procedure FDownloadBegin();    procedure FDownloadComplete();    procedure FCommandStateChange(Command: Integer; Enable: WordBool);    procedure FBeforeNavigate2(const pDisp: IDispatch; var URL, Flags,         TargetFrameName, PostData, Headers: OleVariant; var Cancel: WordBool);  public    constructor Create(AOwner: TComponent); override;    destructor Destroy; override;    procedure GoHome;    procedure GoBack;    procedure GoForward;    procedure GoSearch;    procedure Stop;    procedure Refresh;    procedure CanvasToBuffer(DC: HDC);    procedure Navigate(URL: string);    property DefaultInterface: IDispatch read GetDefaultInterface;    property Document: IDispatch read GetDocument;  published    property Action;    property Align default TAlignLayout.alNone;    property Anchors;    property CanFocus default True;    property CanParentFocus;    property ClipChildren default False;    property ClipParent default False;    property DisableFocusEffect;    property Height;    property Locked default False;    property Margins;    property Opacity;    property Padding;    property Position;    property RotationAngle;    property RotationCenter;    property Scale;    property TabOrder;    property Visible default True;    property Width;    property OnDocumentComplete: TWebDocumentCompleteEvent read FOnDocumentComplete write FOnDocumentComplete;    property OnStatusTextChange: TWebStatusTextChangeEvent read FOnStatusTextChange write FOnStatusTextChange;    property OnTitleChange: TWebTitleChangeEvent read FOnTitleChange write FOnTitleChange;    property OnVisible: TWebVisibleEvent read FOnVisible write FOnVisible;    property OnQuit: TWebQuitEvent read FOnQuit write FOnQuit;    property OnPropertyChange: TWebPropertyChangeEvent read FOnPropertyChange write FOnPropertyChange;    property OnProgressChange: TWebProgressChangeEvent read FOnProgressChange write FOnProgressChange;    property OnNewWindow2: TWebNewWindow2Event read FOnNewWindow2 write FOnNewWindow2;    property OnNavigateComplete2: TWebNavigateComplete2Event read FOnNavigateComplete2 write FOnNavigateComplete2;    property OnFullScreen: TWebFullScreenEvent read FOnFullScreen write FOnFullScreen;    property OnDownloadBegin: TWebDownloadBeginEvent read FOnDownloadBegin write FOnDownloadBegin;    property OnDownloadComplete: TWebDownloadCompleteEvent read FOnDownloadComplete write FOnDownloadComplete;    property OnCommandStateChange: TWebCommandStateChangeEvent read FOnCommandStateChange write FOnCommandStateChange;    property OnBeforeNavigate2: TWebBeforeNavigate2Event read FOnBeforeNavigate2 write FOnBeforeNavigate2;  end;


然后我们构造这个吧

constructor TZYJWebBrowser.Create(AOwner: TComponent);var  Obj: TFmxObject;  R: TRectF;begin  inherited Create(AOwner);  FWebHandle := 0;  Width := 400;  Height := 200;  CanFocus := True;  if not (csDesigning in ComponentState) then  begin    R := AbsoluteRect;    Obj := AOwner as TFmxObject;    while Obj.Parent <> nil do      Obj := Obj.Parent;    if (Obj <> nil) and (Obj is TCustomForm) then      FWebHandle := webCreate(FmxHandleToHWND((Obj as TCustomForm).Handle),                              Visible,                              Round(R.Left),                              Round(R.Top),                              Round(R.Right - R.Left),                              Round(R.Bottom - R.Top));    if FWebHandle = 0 then      raise Exception.Create('创建浏览器组件失败!');    // 下面的绑定浏览器的事件    webDocumentComplete(FWebHandle, FDocumentComplete);    webStatusTextChange(FWebHandle, FStatusTextChange);    webTitleChange(FWebHandle, FTitleChange);    webVisible(FWebHandle, FVisible2);    webQuit(FWebHandle, FQuit);    webPropertyChange(FWebHandle, FPropertyChange);    webProgressChange(FWebHandle, FProgressChange);    webNewWindow2(FWebHandle, FNewWindow2);    webNavigateComplete2(FWebHandle, FNavigateComplete2);    webFullScreen(FWebHandle, FFullScreen);    webDownloadBegin(FWebHandle, FDownloadBegin);    webDownloadComplete(FWebHandle, FDownloadComplete);    webCommandStateChange(FWebHandle, FCommandStateChange);    webBeforeNavigate2(FWebHandle, FBeforeNavigate2);  end;end;

然后我们还要处理组件大小发生改变以使用浏览器大小随之改变

procedure TZYJWebBrowser.Resize;var  R: TRectF;begin  inherited;  if not (csDesigning in ComponentState) then  begin    R := AbsoluteRect;    webMove(FWebHandle, Round(R.Left),                        Round(R.Top),                        Round(R.Right - R.Left),                        Round(R.Bottom - R.Top));  end;end;



浏览器的可视,

procedure TZYJWebBrowser.SetVisible(const Value: Boolean);begin  inherited;  if not (csDesigning in ComponentState) then     webSetVisible(FWebHandle, Value);end;


然后还有各个事件的传递, 这里只举几个例子. 其它都一样

procedure TZYJWebBrowser.FCommandStateChange(Command: Integer; Enable: WordBool);begin  if Assigned(FOnCommandStateChange) then     FOnCommandStateChange(Self, Command, Enable);end;procedure TZYJWebBrowser.FBeforeNavigate2(const pDisp: IDispatch; var URL, Flags,     TargetFrameName, PostData, Headers: OleVariant; var Cancel: WordBool);begin  if Assigned(FOnBeforeNavigate2) then     FOnBeforeNavigate2(Self, pDisp, URL, Flags, TargetFrameName, PostData, Headers, Cancel);end;function TZYJWebBrowser.GetDefaultInterface: IDispatch;begin  Result := webDefaultInterface(FWebHandle);end;function TZYJWebBrowser.GetDocument: IDispatch;begin   Result := webDocument(FWebHandle);end;procedure TZYJWebBrowser.GoBack;begin  webGoBack(FWebHandle);end;

再然后我们就完成了....,看看效果吧



效果还是不错的, 不过有个缺点,当你的父对象被隐藏了, 子窗口确还在,,, 翻看了他所有的代码,也没找着地方有通知事件, 虽然可以有属性可以得到父控件是否隐藏了,  但难不成开个线程或者时钟去监视这个属性么, 我觉得不可行....所以需要自己处理下这个问题



0 0