【问题标题】:How can I avoid refresh with TWebBrowser如何避免使用 TWebBrowser 刷新
【发布时间】:2010-08-10 06:03:39
【问题描述】:

我有一个显示 Google 地图页面的 TWebBrowser 组件。问题是当用户按 F5 时页面刷新和页面重新加载。这会导致 javascript 变量重新初始化并与 Delphi 不同步,并出现脚本错误对话框, 'undefined' 为 null 或不是对象。

我想停止用户刷新。

我为 OnBeforeNavigate2 尝试了这个事件:

procedure TNewOrganizationForm.mapAddressBeforeNavigate2(ASender: TObject;
  const pDisp: IDispatch; var URL, Flags, TargetFrameName, PostData,
  Headers: OleVariant; var Cancel: WordBool);
begin
  inherited;
  Cancel := Assigned(fMapEngine) and not fMapEngine.Loading;
end;

但是当我设置断点时,它甚至没有被调用。还有其他方法吗?

【问题讨论】:

    标签: delphi refresh delphi-2007 twebbrowser


    【解决方案1】:

    Ronald,您可以使用IHTMLDocument2.onkeydown 事件来拦截和阻止密钥。

    要首先分配事件处理程序,您必须使用IHTMLEventObj 作为参数创建一个过程类型。

      THTMLProcEvent = procedure(Sender: TObject; Event: IHTMLEventObj) of object;
    

    那么您必须从InterfacedObjectIDispatch 创建一个类后代来传递和处理事件。

    终于可以这样处理onkeydown事件中截取的key了

    Var
      HTMLDocument2 : IHTMLDocument2;
    begin
        if Not Assigned(WebBrowser1.Document) then  Exit;
        HTMLDocument2:=(WebBrowser1.Document AS IHTMLDocument2);
        if HTMLDocument2.parentWindow.event.keyCode=VK_F5 then //compare the key
        begin
         HTMLDocument2.parentWindow.event.cancelBubble:=True; //cancel the key
         HTMLDocument2.parentWindow.event.keyCode     :=0;
        end;
    end;
    

    //查看完整源代码

    unit Unit55;
    
    interface
    
    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, OleCtrls, SHDocVw, MSHTML;
    
    type
      //Create the procedure type to assign the event
      THTMLProcEvent = procedure(Sender: TObject; Event: IHTMLEventObj) of object;
    
      //Create a  new class for manage the event from the twebbrowser
      THTMLEventLink = class(TInterfacedObject, IDispatch)
      private
        FOnEvent: THTMLProcEvent;
      private
        constructor Create(Handler: THTMLProcEvent);
        function GetTypeInfoCount(out Count: Integer): HResult; stdcall;
        function GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult; stdcall;
        function GetIDsOfNames(const IID: TGUID; Names: Pointer;
          NameCount, LocaleID: Integer; DispIDs: Pointer): HResult; stdcall;
        function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;
          Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult; stdcall;
      public
        property OnEvent: THTMLProcEvent read FOnEvent write FOnEvent;
      end;
    
      TForm55 = class(TForm)
        WebBrowser1: TWebBrowser;
        procedure FormShow(Sender: TObject);
        procedure WebBrowser1NavigateComplete2(ASender: TObject; const pDisp: IDispatch; var URL: OleVariant);
        procedure FormCreate(Sender: TObject);
      private
        { Private declarations }
        FOnKeyDownConnector:  THTMLEventLink; //pointer to the event handler
        procedure WebBrowser1OnKeyDown(Sender: TObject; EventObjIfc: IHTMLEventObj);//the event handler 
      public
        { Public declarations }
      end;
    
    var
      Form55: TForm55;
    
    implementation
    
    {$R *.dfm}
    
    
    constructor THTMLEventLink.Create(Handler: THTMLProcEvent);
    begin
      inherited Create;
      _AddRef;
      FOnEvent := Handler;
    end;
    
    
    function THTMLEventLink.GetIDsOfNames(const IID: TGUID; Names: Pointer; NameCount, LocaleID: Integer; DispIDs: Pointer): HResult;
    begin
      Result := E_NOTIMPL;
    end;
    
    
    function THTMLEventLink.GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult;
    begin
      Result := E_NOTIMPL;
    end;
    
    
    function THTMLEventLink.GetTypeInfoCount(out Count: Integer): HResult;
    begin
      Result := E_NOTIMPL;
    end;
    
    
    function THTMLEventLink.Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer; Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult;
    var
      HTMLEventObjIfc: IHTMLEventObj;
    begin
      Result := S_OK;
      if Assigned(FOnEvent) then FOnEvent(Self, HTMLEventObjIfc);
    end;
    
    
    
    procedure TForm55.FormCreate(Sender: TObject);
    begin
      FOnKeyDownConnector := THTMLEventLink.Create(WebBrowser1OnKeyDown); //assign the address of the event handler
    end;
    
    
    procedure TForm55.WebBrowser1NavigateComplete2(ASender: TObject;  const pDisp: IDispatch; var URL: OleVariant);
    var
      HTMLDocument2      : IHTMLDocument2;
    begin
      HTMLDocument2:=(WebBrowser1.Document AS IHTMLDocument2);
      HTMLDocument2.onkeydown := FOnKeyDownConnector as IDispatch; //assign the event handler
    end;
    
    procedure TForm55.WebBrowser1OnKeyDown(Sender: TObject; EventObjIfc: IHTMLEventObj);
    Var
      HTMLDocument2 : IHTMLDocument2;
    begin
        //finally do your stuff here, in this case we will intercept and block the F5 key.
        if Not Assigned(WebBrowser1.Document) then  Exit;
        HTMLDocument2:=(WebBrowser1.Document AS IHTMLDocument2);
        if HTMLDocument2.parentWindow.event.keyCode=VK_F5 then
        begin
         HTMLDocument2.parentWindow.event.cancelBubble:=True;
         HTMLDocument2.parentWindow.event.keyCode     :=0;
        end;
    end;
    
    
    
    procedure TForm55.FormShow(Sender: TObject);
    begin
    WebBrowser1.Navigate('www.google.com'); 
    end;
    
    
    
    end.
    

    【讨论】:

    • 我还没有时间测试这个,但它看起来不错,你会得到复选标记:)
    • 我已经尝试了上面 RRUZ 的代码并将其链接到内存分析器 FastMM4。我的测试表明存在与 THTMLEventLink 相关的内存泄漏。但是我无法确定 RRUZ 的代码中缺少什么需要添加以解决内存泄漏。有人可以帮忙吗?
    • THTMLEventLink.Create中有多余的_AddRef,会导致内存泄漏。
    • 此外,由于 FOnKeyDownConnector 被声明为 THTMLEventLink 而不是 IDispatch 会负责正确的引用计数,因此会产生内存泄漏问题。
    【解决方案2】:

    我没有找到一个简单的方法来做到这一点。我在 TWebBrowser 上找不到任何会禁用刷新的事件或类似内容。也许你应该检查TEmbededWB,因为它有更多的事件并且比默认的 TWebBrowser 更有能力。否则它们非常相似。

    但我找到了防止刷新的方法。现在有趣的是,即使在主窗体上将 KeyPreview 设置为“True”,我也无法收到关键通知。似乎 TWebBrowser 以某种方式吃掉了它们。但这有效:

    procedure TForm1.FormCreate(Sender: TObject);
    begin
      Application.OnMessage := OnAppMessage;
    end;
    
    procedure TForm1.OnAppMessage(var Msg: TMsg; var Handled: Boolean);
    begin
      if Msg.message = WM_KEYDOWN then
        if Msg.wParam = VK_F5 then
          Handled := True;
    end;
    

    不是最优雅的方式,但至少它有效。我还没有找到更好的解决方案。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-01-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-23
      • 2015-05-29
      • 1970-01-01
      相关资源
      最近更新 更多