MSHTML Hosting – Building UI’s

My last posts have dealt with using the WebBrowser component to display HTML pages inside your application. We have seen how it does not take much to embed the control to create your own little mini-webbrowser. In this post I want to go a little further than building a webbrowser. Lots of applications are using a web-like UI. Applications like Intuit Quicken, Microsoft Outlook and Money actually use the WebBrowser control to achieve their web-like UI’s. Microsoft Office task panes and other inductive UI’s can be developed using the WebBrowser control.

When using the WebBrowser control for building a UI there are a few issues to consider:

  1. Loading the HTML into the control. You most likely will not be using Navigate to load the HTML. In most cases you are dynamically generating the HTML from scratch or from a template loaded from a resource.
  2. Handling mouse and keyboard events. Since you are building a UI, it is very likely that the content will be interactive. There may be edit boxes, push buttons and hyperlinks in the content. You will need to handle those events and respond accordingly.

Loading HTML

The most common (but not the only) way to load HTML from a buffer to the WebBrowser is via streams. Microsoft has a nice example in the WebBrowser reference. A quick Google search will turn up a way to do it in your favorite tool or language. The key points here are:

  1. Navigate to “about:blank”
  2. Wait for the blank document to finish loading
  3. Load your HTML using the IPersistStreamInit method

Step #3 looks like this:


IHTMLDocument2* pDoc = ...;
IStream* pMyStream = ...;

IPersistStreamInit* pPersist = 0;
HRESULT hr = pDoc->QueryInterface(IID_IPersistStreamInit, (void**)&pPersist);
if (SUCCEEDED(hr) && pPersist) {
    hr = pPersist->InitNew();
    if (SUCCEEDED(hr)) {
        hr = pPersist->Load(pMyStream);
    }
    pPersist->Release();
}

Note: I would strongly recommend that any external links you have in your generated HTML (stylesheets or JavaScript) are referenced using absolute paths. Do not use relative paths. The IPersistStreamInit method does not update the control’s base URL. Navigate does update the base URL. Therefore, any relative path will have “about:blank” prepended to it and the control will not likely find your external link.

Handling Events

The ability to handle mouse and keyboard events is critical when creating an interactive UI. There are 2 basic methods to do this with WebBrowser:

  1. Use <A> tags to create hyperlinks with bogus href URL’s. Then use OnBeforeNavigate2 to intercept the bogus URL, cancel the navigation and respond to the mouse click.
  2. Hook your native code to the onclick, onkeypress, or any of the many other JavaScript events.

Method #1 is cheap and easy, but really only works with hyperlinks. In my applications, I create bogus hyperlinks that are easy to parse inside OnBeforeNavigate2 and contain breadcrumbs for me to use when responding to the click. Here is an example:

myapp://edititem/12345

I can look for a constant substring to indicate its my bogus HREF (myapp://). I can figure out the type of action (edititem). I also know which item to edit (12345). The last part could be a database ID or a pointer to an object cast to a long integer. The whole thing even looks like a real URL too.

Method #2 is much more robust, but a little more complicated to implement. There are more steps involved. You will also be working with the IHTMLDocument system and creating IDispatch wrappers. We’ll cover those topics in future posts.