如何防止 Delphi TWebBrowser窃取焦点

t2a7ltrp  于 12个月前  发布在  其他
关注(0)|答案(2)|浏览(123)

我对 Delphi 和网络浏览器组件很着迷。
我创建了一个简单的应用程序,在Memo中输入HTML并在WebBrowser组件中显示结果。
但是,当我在WebBrowser内部单击时,每次HTML代码更新(在备忘录中)都会导致WebBrowser组件窃取焦点。
以下是重现问题的一步一步:

  • 创建新的VCL应用程序
  • 添加一个TWebBrowser组件来显示html
  • 添加一个TMemo组件来输入html代码
  • 在Memo1.OnChange事件中插入以下内容:
if WebBrowser1.Document = nil then
begin
  WebBrowser1.Navigate('about:blank');
  while WebBrowser1.ReadyState <> READYSTATE_COMPLETE do
    Application.ProcessMessages;
end;

ms := TMemoryStream.Create;
try
  Memo1.Lines.SaveToStream(ms);

  ms.Seek(0, 0);

  (WebBrowser1.Document as IPersistStreamInit).Load(TStreamAdapter.Create(ms)) ;
finally
  ms.Free;
end;
  • 运行应用程序
  • 在备忘录中输入HTML
<html>
<body>
Hello WebBrowser
</body>
</html>
  • 在WebBrowser内容中单击
  • 回到备忘录,尝试在HTML中键入更多更改
    ***我们开始了 *,每按一个键,浏览器组件就会为它窃取焦点!
    我如何解决这个问题并防止“窃取焦点”?

注:唯一的解决方法是在WebBrowser上单击**后按TAB键,这可以防止WebBrowser通过备忘录在html代码中的新更改后窃取焦点。

已使用此解决方法解决。

将Memo1.OnChange代码更改为:

procedure TForm1.Memo1Change(Sender: TObject);
  var
    ms: TMemoryStream;
  begin
    LockWindowUpdate(panel1.Handle); // fix: lock webbrowser parent updates

    // fix: re-set the webbrowser parent to prevent focus stealing
    TControl(WebBrowser1).Parent := nil; 
    TControl(WebBrowser1).Parent := panel1;
    // fix:eof

    if WebBrowser1.Document = nil then
    begin
      WebBrowser1.Navigate('about:blank');
      while WebBrowser1.ReadyState <> READYSTATE_COMPLETE do
        Application.ProcessMessages;
    end;

    ms := TMemoryStream.Create;
    try
      Memo1.Lines.SaveToStream(ms);

      ms.Seek(0, 0);

      (WebBrowser1.Document as IPersistStreamInit).Load(TStreamAdapter.Create(ms)) ;
    finally
      ms.Free;
    end;

    LockWindowUpdate(0); // fix: unlock webbrowser parent updates to prevent flicking!!
  end;
btxsgosb

btxsgosb1#

在表单中添加一个复选框和另一个备忘录Memo 2,并将其OnChange指向Memo 1的处理程序。然后,更改您的处理程序,如下所示。你应该会发现,即使在使用Memo 1引发焦点窃取之后,当你输入Memo 2时(当然,复选框被选中),也不会发生这种情况。
我意识到我用Memo 2所做的与你所做的不太一样,但它可能更好,因为备忘录不需要包含HTML文档的外部结构。这取决于你在做什么,这可能是一个优势或劣势。
Fwiw,在点击WB后调用ShowCaret(Memo1.Handle)返回false - GetLastError/SysErrorMsg report 5/Access Denied,所以我猜点击WB会以某种方式破坏Memo 1的插入符号-我的意思是比你期望的要多。我还在调查

procedure TForm1.Memo1Change(Sender: TObject);
var
  ms : TMemoryStream;
begin
  if WebBrowser1.Document = nil then
  begin
    WebBrowser1.Navigate('about:blank');
    while WebBrowser1.ReadyState <> READYSTATE_COMPLETE do
      Application.ProcessMessages;
  end;

  try
    if CheckBox1.Checked then begin
      (WebBrowser1.Document as IHtmlDocument2).Body.innerHtml := Memo2.Lines.Text;
    end
    else begin
      ms := TMemoryStream.Create;
      try
        Memo1.Lines.SaveToStream(ms);
        ms.Seek(0, 0);
        (WebBrowser1.Document as IPersistStreamInit).Load(TStreamAdapter.Create(ms)) ;
      finally
        ms.Free;
      end;
    end;
  except
    // silence any exceptions raised when using CheckBox1.Checked = True
  end;
end;

更新:

如果你更喜欢更接近你原来的东西,而不是我上面的建议,下面是更紧凑,似乎运行得更快一点:

procedure TForm1.HandleMemo1ChangeNew(Sender : TObject);
var
  Doc : IHtmlDocument2;
  V : OleVariant;
begin
  if WebBrowser1.Document = nil then
    WebBrowser1.Navigate('about:blank');

  Doc := WebBrowser1.Document as IHTMLDocument2;
  V := VarArrayCreate([0, 0], varVariant);
  V[0] := Memo1.Lines.Text;
  Doc.Write(PSafeArray(TVarData(v).VArray));
  Doc.Close;
end;

procedure TForm1.WebBrowser1NavigateComplete2(Sender: TObject;
  const pDisp: IDispatch; var URL: OleVariant);
begin
  WebBrowser1.Enabled := False;
end;
798qvoo8

798qvoo82#

我发现解决这种情况的唯一方法是采用以下解决方案:
1.将TButtonTEdit添加到WEBBrowser表单。
1.将这些组件保留在窗体的可见区域之外,并为两者的Top属性指定负值(保留属性Visible=True)。

  • 在按钮的OnClick事件中,添加:
  • Edit1.SetFocus;
  • Edit1.Text:=DateTimeToStr(Now);
  • 在WebBrowser的CommandStateChange事件中,添加:
  • ToolButton1.Click;

相关问题