WebView2 Focus Issues in Legacy Delphi Projects

Summary

Embedding a WinForms window that hosts WebView2 inside a legacy Delphi 7 panel via SetParent displays the page, but keyboard input is routed to the original Delphi control instead of the web page. Mouse interactions work, the caret appears, yet typing goes to the previously focused Delphi edit box.

Root Cause

  • Message routing mismatch: SetParent changes the visual hierarchy without updating the input‑focus chain.
  • WS_CHILD vs. WS_POPUP: Converting a popup window to a child breaks the default focus handling that WebView2 relies on.
  • Thread‑affinity of WinForms: The WinForms message loop runs on its own UI thread; after re‑parenting, the keyboard messages (WM_KEYDOWN/WM_CHAR) are still being dispatched to the Delphi thread’s window procedure.
  • Missing focus bridge: WebView2 expects to receive focus notifications through SetFocus on its HWND. The Delphi host never forwards these after the re‑parenting.

Why This Happens in Real Systems

  • Legacy UI frameworks (Delphi VCL) often embed newer components using raw WinAPI calls.
  • Developers treat SetParent as a “magic” way to compose windows, ignoring that focus and activation are not automatically transferred.
  • Modern controls like WebView2 depend on COM’s UI Automation and keyboard layout handling, which are disrupted when the control is not the top‑level window that created the UI thread.
  • Cross‑thread window hierarchies are unsupported; the OS will send keyboard messages to the thread that owns the foreground window (Delphi), not the child WinForms window.

Real-World Impact

  • Users cannot fill forms, send chat messages, or interact with any text‑based UI inside the embedded browser.
  • Customer support tickets spike with “typing does nothing” complaints.
  • Automated UI tests that simulate typing fail, breaking CI pipelines.
  • In security‑sensitive apps, lost keyboard focus can lead to input hijacking where keystrokes are captured by the wrong control.

Example or Code (if necessary and relevant)

// Ensure the WinForms window receives focus after being re‑parented
public void SetAsChild(IntPtr hostHandle)
{
    var childHandle = this.Handle;
    WinApi.SetParent(childHandle, hostHandle);
    WinApi.SetWindowLong(childHandle, WinApi.GWL_STYLE,
        (int)(WinApi.GetWindowLong(childHandle, WinApi.GWL_STYLE) & ~WinApi.WS_POPUP | WinApi.WS_CHILD));
    WinApi.SetFocus(childHandle);               // <-- critical
}
// Delphi side: forward focus when the panel receives it
procedure TForm1.pnlAgenteHostEnter(Sender: TObject);
begin
  if Assigned(FAgenteHandle) then
    Winapi.Windows.SetFocus(FAgenteHandle);
end;

How Senior Engineers Fix It

  • Avoid SetParent for UI composition; instead host the WebView2 control directly in Delphi using COM interop or the WebView2 Delphi wrapper.
  • If SetParent must be used:
    • Call SetFocus on the WinForms HWND whenever the Delphi container gains focus.
    • Ensure both windows run on the same UI thread (use AttachThreadInput).
    • Reset the window style to include WS_TABSTOP and remove WS_EX_NOACTIVATE.
    • After re‑parenting, force a focus change: SendMessage(child, WM_SETFOCUS, 0, 0);.
  • Initialize WebView2 with IsInputEnabled = true (default) and verify CoreWebView2Controller.IsFocusable is true.
  • Validate the message loop: call Application.DoEvents() in the WinForms app after every focus change to let the dispatcher process pending input messages.
  • Write unit tests that simulate focus transfer and keystrokes to catch regressions.

Why Juniors Miss It

  • Assume visual nesting equals input nesting – they see the child inside the parent and think focus follows automatically.
  • Over‑reliance on designer tools; they never inspect the low‑level window styles or message flow.
  • Lack of experience with cross‑thread UI ownership and the nuances of SetParent.
  • Tend to debug only the visible symptom (no typing) and look for JavaScript errors, ignoring the WinAPI layer.
  • Miss the need to explicitly set focus after re‑parenting because the documentation for SetParent emphasizes only visual composition.

Leave a Comment