I'm going crazy with Delphi and WebBrowser component.
I created a simple application, to type HTML in a Memo and display the result inside the WebBrowser component.
But, when I click inside the WebBrowser, each HTML code update (in memo) results in the steal of the focus by the WebBrowser component.
Here is the step-by-step to reproduce the problem:
insert on Memo1.OnChange event this:
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;
run the application
type the HTML inside the Memo
<html>
<body>
Hello WebBrowser
</body>
</html>
click inside the WebBrowser content
How can I solve this and prevent the "steal of the focus" ?
Ps.: the only workaround is pressing the TAB key after clicking on WebBrowser, this prevent the webbrowser the steal the focus after new changes in html code via memo.
Solved with this workaround.
Change the Memo1.OnChange code to this:
  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;
Add a checkbox and another memo, Memo2, to your form, and point its OnChange at your handler for Memo1. Then, change your handler as shown below. You should find that even after provoking the focus-stealing using Memo1, it doesn't happen when you type into Memo2 (with the checkbox checked, of course).
I realise that what I'm doing with Memo2 isn't quite the same as what you are, but it may be preferable in that the memo isn't required to contain the outer structure of the HTML document. Depending on what you're doing, that may be an advantage or a disadvantage.
Fwiw, calling ShowCaret(Memo1.Handle) after clicking in the WB returns false - GetLastError/SysErrorMsg report 5/Access Denied, so I'm guessing that clicking in the WB somehow clobbers Memo1's caret - I mean more than you'd expect just from shifting focus. I am still looking into that.
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;
Update:
If you'd prefer something closer to your original than my suggestion above, the following is more compact and seems to run a bit faster:
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;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With