Wenn man ein Web Browser Control einbindet und dieses eine Seite lädt, dann wird der Fokus in dieses Browser Control gesetzt. Dagegen ist kein Kraut und keine Notification gewachsen.
Genaugenommen ist nicht das Webbrowser Control schuld, sondern der Scriptcode der auf der Seite läuft und den Fokus umsetzt. Das sieht man schnell wenn man den Moment der WM_KILLFOCUS Nachricht im Debugger abpasst und sich den Stacktrace ansieht.
0013b84c 4a570824 USER32!NtUserSetFocus 0013b858 4a5ce628 mshtml!CDoc::TakeFocus+0x2a 0013b880 4a63fc0b mshtml!CElement::BecomeCurrent+0x167 0013b8b4 4a63fb72 mshtml!CElement::focusHelper+0xcc 0013b8c0 4a587c85 mshtml!CElement::focus+0x1d 0013b8cc 4a5d7477 mshtml!Method_void_void+0x17 0013b94c 4a57fae8 mshtml!CBase::ContextInvokeEx+0x462 0013b97c 4a575413 mshtml!CElement::ContextInvokeEx+0x72 0013b9b0 76fa5295 mshtml!CElement::ContextThunk_InvokeEx+0x44 0013b9e8 76fa5208 jscript!IDispatchExInvokeEx2+0xa9 0013ba20 76fa5323 jscript!IDispatchExInvokeEx+0x56 0013ba90 76fa577b jscript!InvokeDispatchEx+0x78 0013bad8 76fa57c6 jscript!VAR::InvokeByName+0x1c1 0013bb18 76fa4ab0 jscript!VAR::InvokeDispName+0x43 0013bb3c 76fa5a14 jscript!VAR::InvokeByDispID+0xfb 0013bd30 76fa46d8 jscript!CScriptRuntime::Run+0x195b 0013bdf4 76fa506e jscript!ScrFncObj::Call+0x69 0013be6c 76fa5f6a jscript!CSession::Execute+0xb8 0013bf6c 76fa672f jscript!NameTbl::InvokeDef+0x183 0013c040 76fa5295 jscript!NameTbl::InvokeEx+0xd2
Dummerweise gibt es kein Event mehr, das danach gefeuert wird, wenn der Skript-Code abläuft. Das letzte Event bevor das aktive Fenster den Fokus verliert ist OnDocumentComplete.
Es gibt auch einige Threads die dieses Thema behandeln, aber keine vernünftige Lösung. Von so manchen Timerlösungen halte ich nichts, die da so vorgeschlagen werden, wer weiß schon wann eine Seite geladen ist?
Besonders ärgerlich auch, wenn man das Browser Control nicht mal auf einem sichtbaren Fenster hat, sondern nur in einem versteckten Fenster hält. Auch in diesem Fall verliert das aktive Fenster den Fokus.
Aber mit einem kleinen Trick bekommt man es doch hin ( 🙂 warum sonst schreibe ich den Artikel )
- Man baut einen OnDocumentComplete Handler ein.
- Wenn das Event eintritt, besorgt man sich mit GetFocus das Fenster, dass aktuell noch den Fokus inne hat.
- Nun sendet man mit PostMessage eine selbst definierte Nachricht (#define WM_RESTOREFOCUS (WM_APP+x)) an den Container des Webbrowser Controls und übergibt als wParam einfach das Handle des Fensters, dass man soeben mit GetFocus ermittelt hat.
- Nach diesem Event wird der Skript-Code ausgeführt, der den Fokus stiehlt. Das stört uns nicht.
- Irgendwann kommt die Messageloop jetzt wieder an die Reihe und zieht die benutzerdefinierte Nachricht WM_RESTOREFOCUS aus der Queue.
- Man hat natürlich einen Handler für diese Nachricht im Container des Webbrowser Controls. Dieser macht nun nichts anderes als einen SetFocus auf das HWND Handle auszuführen, das im wParam übergeben wurde. Ein Test zuvor mit IsWindow versteht sich von selbst.
Dadurch, dass die Nachricht in der Message-Queue gepostet wird, wird sie zeitnah ausgeführt sobald wirklich der User wieder eine Chance selbst Eingaben zu machen. Problem zufriedenstellend gelöst.
Das sollte sich sogar mit C# oder VB hinbekommen lassen 😉