Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
Markdown Monster - The Markdown Editor for Windows

Web Browser HTML Editing Focus issue resolved finally


:P
On this page:

Ugh, finally managed to find a solution to the Web Browser Control HTML Editing focus problems that I’ve been having. It turns out the solution was a lot easier although tricky than I had original thought.

 

The issue is as follows: I’m using the Web Browser Control and use HTML Editing by setting designMode in the document to .t.. Once in edit mode context switches to another application – then back to Help Builder – caused the Editor control to loose its keyboard input focus. The control activated but there’d be no cursor and you couldn’t type into the control. This using Visual FoxPro by the way but this edit focus problem also manifests in other ActiveX container environment so the solution here might work with other environments as well.

 

The first problem in FoxPro at least is that FoxPro doesn’t fire any events for application context switches. It does fire LostFocus() and GotFocus() events for window switches of the same application (ie. multiple top level or MDI windows), but not if you switch off to another application or return back from another application. This likely is also the source of the ActiveX activation problem.

 

Luckily in VFP 9, FoxPro introduced event binding to Windows Messages, so you can now easily hook Windows Messages to any FoxPro form. The code below hooks the WM_ACTIVATEAPP event which is fired both when focus leaves the current application and gets focus back from another application:

 

SET PROCEDURE TO WinEvents ADDITIVE

#INCLUDE WINEvents.h

loEventHandler = CREATEOBJECT("wwHelpWinEvents")

BINDEVENT(goHelp.hWnd, WM_ACTIVATEAPP,loEventHandler, "WM_ACTIVATE_Handler")

 

BINDEVENT works by hooking the Hwnd of a form or control (ActiveX controls often have Hwnds) and specifying which message to handle and to which object and method to fire it to. I’ve used the above handler quite extensively in several of my applications – this has been a pretty big missing feature in VFP that we can now work around with Windows Event binding.

 

To handle this event I implemented a class and a method within it.

 

#INCLUDE wwHelp.h

#include WinEvents.H

 

*************************************************************

DEFINE CLASS wwHelpWinEvents AS Custom

*************************************************************

 

PROTECTED nOldProc, lHtmlEditOnLostFocus

nOldProc = 0

lHtmlEditOnLostFoucs = .f.

 

************************************************************************

* WinEvents :: Init

****************************************

FUNCTION Init(lnHwnd)

 

IF EMPTY(lnHwnd)

   lnHwnd = _SCREEN.hWnd

ENDIF

 

*** Need to keep track of the original WinProc

declare integer GetWindowLong in Win32API ;

        integer hWnd, integer nIndex

 

This.nOldProc = GetWindowLong(lnHwnd, GWL_WNDPROC)

ENDFUNC

 

************************************************************************

* WinEvents :: WM_ACTIVATE_Handler

****************************************

***  Function: Handler for App activation from external Apps

***    Assume:

***      Pass:

***    Return:

************************************************************************

FUNCTION WM_ACTIVATE_Handler(hWnd, Msg, wParam, lParam)

 

IF VARTYPE(goHelp) = "O"

   loException = null

   TRY

      IF wParam = 0  && DeActivation set flag

         *** Set flag if HTML Editor is active

         IF TYPE("goHelp.oViewer.pages[goHelp.oViewer.ActivePage].ActiveControl") = "O" AND ;

            LOWER(goHelp.oViewer.PAGES[goHelp.oViewer.ActivePage].ACTIVECONTROL.CLASS)="wwbrowser_editor"

            this.lHtmlEditOnLostFocus = .t.

         ENDIF

      ELSE

         *** Only handle if HTML Editor was active on lost focus

         IF this.lhtmleditonlostfocus

            this.lHtmlEditOnLostFocus = .f.

           

            *goHelp.oViewer.pgBody.tmrHtmlEditActivator.Interval = 50

 

            *** Fire off a timer and call SetEditFocus in 50ms

            goHelp.oViewer.pgBody.oHtmlEdit.SetEditFocus()

           

            *** IMPORTANT:

            *** Focus another control so the form activates properly

            *** and the deactivation fires on the Web Browser

            goHelp.oViewer.pgBody.txtTopic.SetFocus() 

         ENDIF

      ENDIF  

   CATCH TO loException

      MESSAGEBOX("Non fatal error handling Window reactivation" + CHR(13) + CHR(13) +;

                 loException.Message,48,WWHELP_APPNAME) 

   ENDTRY  

ENDIF

 

*** Redeclare here to make 100% sure it's here!

DECLARE INTEGER CallWindowProc in Win32API ;

   integer lpPrevWndFunc, integer hWnd, integer Msg, integer wParam, ;

   integer lParam

 

*** Must call back original WinProc

RETURN CallWindowProc(THIS.nOldProc, hWnd, Msg, wParam, lParam)

ENDFUNC

 

ENDDEFINE

 

This code sets up an event handler method that is called in response to the WM_ACTIVATEAPP request. The Init finds the original WinProc so that we can call it back when we handle our windows message event. If we don’t call back the event gets eaten and that would be bad since the app wouldn’t activate/deactivate.

 

The actual handler is implemented in the WM_ACTIVATE_Handler method. The WM_ACTIVATEAPP method is fired to our application whenever focus is lost (wParam = 0) or when focus returns to us from another window (wParam = the other window’s hWnd ie. non-zero). I need to capture events because I need to make sure that a flag gets set only when the HTML Edit control is active. Otherwise we don’t need any of this code and simply want to forward directly to the original WinProc.

 

In Help Builder the Editor is buried in a PageFrame Page, so the check for making sure that the control is active is a bit lengthy. Once the flag is set the next activation of Help Builder will cause this method to fire again, so this is the hook needed to set the focus.

 

After all the pain I went through originally to try and activate the Html Editor (see previous entry), the final solution was pretty simple. The problem with all the previous approaches was the simple fact that this method fires before the form is actually activated by VFP’s internal event handling. IOW, VFP doesn’t know the form is alive and any call to SetFocus() at this point has really no effect. Even using various Windows events doesn’t work because VFP reinitializes the form when it finally gets control and do it’s normal window processing.


So the solution here is to not send the SetFocus() call directly, but rather delay it with a timer. This gives VFP a chance to get ready. In addition the code fires a SetFocus() call on another control to force VFP deactivate the Web Browser control first. Then the timer kicks in after 50 milliseconds and changes the focus back to the Web Browser’s Edit interface. And that actually works!

 

The Web Browser Editor class implements the SetEditFocus method that creates a custom timer object, so that that this functionality is somewhat encapsulated.

 

FUNCTION SetEditFocus()


IF TYPE("THIS.PARENT.oHtmlEditTimer") # "O"

   THIS.Parent.AddObject("oHtmlEditTimer","HTMLEditTimer")

ENDIF

 

THIS.parent.oHtmlEditTimer.Interval = 50

 

The timer is a custom timer, and the timer method doesn’t doesn’t do anything fancy:

 

*** This event acts as a delayed SetFocus to the

*** HtmlEditControl

THIS.Interval=0

THIS.Parent.oHtmlEdit.SetFocus()

 

So, the solution is convoluted and requires a bit of logic scattered over a few objects unfortunately and some app specific logic in these components. It will be interesting to see whether I can remember how this works in a few months down the road .

 

But heck that’s what this BLOG entry is for.

 

Thanks to Doug Hennig and Calvin Hsia for a few helpful hints...


The Voices of Reason


 

Craig McGuff
April 03, 2005

# re: Web Browser HTML Editing Focus issue resolved finally

Fantastic! This has been bugging me in the last few days. Similar problem with an activex control where you only see half the control until you physically click on it with the mouse. The only thing that worked was putting a VFP control before it in the tab order and doing a
Control.SetFocus()
KEYBOARD '{TAB}'

sort of thing. This solution is a lot more elegant.

Tristan Leask
April 29, 2005

# re: Web Browser HTML Editing Focus issue resolved finally

The web browser control has a hwnd property doesn't it? For some reason it doesn't seem to contain anything. Have you had a similar issue as well?

CalvinH
July 25, 2005

# re: Web Browser HTML Editing Focus issue resolved finally


zafer
February 18, 2006

# re: Web Browser HTML Editing Focus issue resolved finally

firsht i thank for your information
and for your helpping.
how can i do strtofile("asd.txt","//*.html")detail a page of html from internet(code of html from site)...if you help me i will be more happy.
for help
i thank you

Rick Strahl
October 05, 2006

# Need a little help with Web Browser Control editing - Focus issues - Rick Strahl

I've hit a snag in my Html Edit Control integration in Help Builder where focus to the control is lost and cannot be returned properly into the active cursor position. When tabbing off to another app and returning focus returns to the control, but the control is no longer in live edit mode.

Rick Strahl's Web Log
February 17, 2007

# Need a little help with Web Browser Control editing - Focus issues - Rick Strahl's Web Log

I've hit a snag in my Html Edit Control integration in Help Builder where focus to the control is lost and cannot be returned properly into the active cursor position. When tabbing off to another app and returning focus returns to the control, but the control is no longer in live edit mode.

Calvin Hsia's WebLog
July 09, 2007

# Calvin Hsia's WebLog : ActiveX Controls don't treat Focus the same

If a RichText control on a form doesn’t have focus yet, it responds to being clicked on by calling the IOleControlSite::OnFocus method inside VFP. The VFP code says Aha!: there is a focus change happening, so it fires the LOSTFOCUS event for the control

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024