Suppressing F5 Refresh in MFC’s CDHtmlDialog

OK, so you’re all giddy about this new HTML thing — now you can make dialogs with just HTML markup!  Great, so you’ve got your MFC application and you write your own dialog class that derives from CDHtmlDialog, you create your markup for the dialog and you’re all set.  Then you let your testers have at it and they discover that pressing F5 invokes the embedded web browser control’s refresh operation and your page displays wrong.  What to do?  Well, you have a couple choices: 1) you can figure out how to reliably refresh the HTML from the application, or 2) you can suppress the refresh operation because its essentially meaningless, since any updates needed to the HTML will be done directly by the application.

OK, so you pick option 2.  How do you implement that?  You could always gobble the key event at a low level, but that seems like a fairly blunt attack on the problem.  Is there another way?  Yes, there is, but unfortunately the relevant documentation in MSDN is a bit sketchy, so I’ll show you how I solved it and maybe someone can show me an even better way than what I’ve done.

There is a little interface called IDocHostUIHandler that allows an application hosting a web browser control to customize the behavior of the web browser’s menus, toolbars and context menus.  It also lets you change the behavior of accelerators through its TranslateAccelerator method.  Here’s where MSDN gets a bit sketchy.  The example code they have for this method doesn’t match the signature of the method!  Furthermore, there isn’t a really good description of how this method controls behavior of the accelerator.  It turns out that if you want the accelerator ignored, you return a failing HRESULT from this method, otherwise you return a successful HRESULTCDHtmlDialog implements this interface, so to change the behavior, you override the virtual method TranslateAccelerator:

bool IsRefreshKeyMessage(const MSG *message)
    return message
        && (message->message == WM_KEYUP)
        && (message->wParam == VK_F5);

CMyDialog::TranslateAccelerator(MSG *message, const GUID *, DWORD)
    return IsRefreshKeyMessage(message) ? E_FAIL : S_OK;

That little function that does the test is an example of intentional programming — we took a complex boolean expression and extracted it into a function with a name that reveals the intention of the boolean expression. Now when someone looks at the implementation of TranslateAccelerator, they know the intention of the programmer who wrote the code. By injecting a function with an intention revealing name, I get closer to that nirvana of “self documenting code”.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: