Processing Key presses in an HTMLControl

Ever had one of those coding experiences when nothing seems to be working? And then suddenly it all fits into place, and suddenly you're doing the Programmers Victory Dance? I've just had one of those, and it was such a fiddly little thing that I wanted to write it down quickly in case I forgot about it, or if anyone in the same boat happens to be Googling/MSN Searching one day and comes across it. I'm only going to cover the annoying part, and put the rest in a Whitepaper that will one day grace MSDN (I hope).

 

I was tasked with creating a Windows Mobile 5.0 Smartphone application for displaying text and graphics. I decided to write the application in C++, and make use of the HTMLControl to do the actual displaying. The application would display several pages of text, each with links to other pages. The user could navigate by selecting a new link with the navigation control, or by pressing the left or right softkeys to move to the previous and next pages.

 

The problem was that initially, once the first page was displayed, there was no link highlighted by default. It would be possible to use JavaScript to highlight a link, but it was aesthetically more pleasing that no link was highlighted at least at the start.

 

Unfortunately, I couldn't find a way to get the HTMLControl to divulge if it was currently highlighting a link. So, to be cunning, by default I assumed that no link was highlighted, until the user moved the navigation key up or down. Once they moved the key, we could assume the first or last link would be highlighted. In other words, my application can to detect a key press - but for the life of me, I couldn't work out how to detect key presses when the HTMLControl was active.

 

Putting a test for WM_KEYUP (or WM_KEYDOWN) in the WinProc just didn't work. No keyboard messages were reaching it. I thrashed around for a while, until someone said "Subclassing". Things started to look up.

 

By subclassing the HTMLControl window, I could get first look at all the messages it was sending. So I subclassed the control, and created a new version of WinProc to handle the messages, like this:

 

 

// Initialize the HTMLControl

if (!InitHTMLControl(g_hInst))

    return FALSE;

 

// Create the Control

 

     RECT rc2;

     GetClientRect(hWnd,&rc2);

 

     g_HTMLView = CreateWindow(DISPLAYCLASS, L"QuickStart Tour", WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS, rc2.left, rc2.top, rc2.right-rc2.left,rc2.bottom-rc2.top, hWnd, NULL,g_hInst, NULL);

 

 

// Subclass the HTML Control so that we can get the keyboard messages.

 

 

     lpfnOldProc = (WNDPROC)GetWindowLong(g_HTMLView ,GWL_WNDPROC );

 

     if (0==SetWindowLong(g_HTMLView, GWL_WNDPROC, (LONG) HTMLProc))

     {

      // Error subclassing the control.

      return FALSE;

     }

 

LRESULT CALLBACK HTMLProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

{

      // This is the function called by the subclassed HTMLView.

 

      switch(message)

      {

 

        

           case WM_KEYDOWN:

 

       ...

           break;

       }

 

       // Carry on processing the keypress and other messages, thanks.

       return CallWindowProc(lpfnOldProc,hWnd,message,wParam,lParam);

}

 

Well, this didn't work. It looked very much like it should work, but messages were sent to HTMLProc very rarely - a few at the start of the application, and a few at the end.

 

More head scratching.

 

Then I found a great serious of articles: 

It was this last article that gave me the fix. It turns out that the HTMLControl consists of two windows, and I was subclassing the wrong one. I needed to subclass the child window of the control, like this:

 

// Subclass the HTML Control so that we can get the keyboard messages.

 

      hWndInternal = GetWindow(g_HTMLView, GW_CHILD);

      SetFocus(hWndInternal);

 

      lpfnOldProc = (WNDPROC)GetWindowLong(hWndInternal,GWL_WNDPROC );

 

      if (0==SetWindowLong(hWndInternal, GWL_WNDPROC, (LONG) HTMLProc))

      {

       // Error subclassing the control.

       return FALSE;

      }

 

 

And that's all there was to it. Victory Dance!