MSHTML Hosting – Drawing On WebBrowser

Hosting the WebBrowser in your application makes it easy to create slick-looking textual UI’s. HTML is a fairly expressive markup language and the results can be very professional with little difficulty. The graphical side is a little less impressive. Thankfully, SVG and VML (native support in the WebBrowser control) go a long way to fill the gap. Both have capabilities that blow away good old Win32 GDI drawing. Even so, there may be times that you wish you could grab an HDC for the WebBrowser control and start drawing on it. The WebBrowser control actually has a system built into it that allows you to do just that.

IHTMLPainter is part of a system in MSHTML called Binary Behaviors. Behaviors can do lots of cool stuff. Painters are a special kind of behavior called a rendering behavior. IHTMLPainter::Draw is called by MSHTML before or after the HTML content is rendered allowing you to draw below or above the content. MSHTML passes an HDC into method that you can use with any GDI or GDI+ drawing function. Basically, behaviors are COM objects that you implement and are associated with HTML elements, either in the HTML itself using CSS or progammatically using addBehavior. The MSDN has a nice tutorial on creating a rendering behavior that includes sample code.

XML Services In Desktop Applications

It’s pretty fair to say that I’d like to change the way I write desktop applications. I am tired of the UI framework lockin that comes with traditional desktop development. I’ve posted a couple times about using DHTML+SVG to create a desktop application UI. I have been working on prototypes in DHTML+SVG, as well as Laszlo (server-less deployment is now available) and Xamlon (they have a cool new flash-based system in addition to the .NET system).

I am now focusing my attention on non-UI parts of desktop applications and how Web-like techniques could be applied. Desktop applications, large ones specifically, are usually composed of subsystems that are exposed to the user via a host client. IMO, it’s important to componentize these subsystems and get them to interact in a way that does not tightly couple them together. Sounds a lot like the definition of a Web service. One way of doing this in C++ is using abstract base classes as interfaces. I am beginning to find this method too object-oriented for my tastes. The sheer number of interfaces required to support the subsystem can be quite large. Interfaces usually cause a language lockin as well. C++ abstract base classes cannot be used very easily by other languages, unless your willing to completely wrap your interfaces with proxy code.

I am leaning more towards an XML services approach. Each subsystem would have a single entry point which would dispatch XML messages (in a RESTful sorta way) throughout the rest of the subsystem. This approach feels just like Web-based services and has the same kind of benefits. Just about any language can immediately start using the subsystem with minimal effort. The subsystem’s dispatch entry point could be made HTTP-aware and suddenly gain the benefits of a true Web service:

  • Subsystems can be put on different machines.
  • HTTP techniques can be used to increase scalability and robustness.
  • Many different and special-use clients can be created which leverage the subsystem.

The kind of subsystems I am referring to include parsers, graph renders, expression evaluators, report generators, data converters and data analysis systems. These are examples of systems that should be made well-defined islands and have great potential for reuse. Instead, they are locked up into the UI client and can not be integrated into other solutions.

MSHTML Hosting – More Tricks

Here are a couple miscellaneous tips for using the WebBrowser while in design (edit) mode.

Setting Focus In Design Mode

With the WebBrowser control in design mode, there are times that focus (and the blinking cursor) are not set correctly. For example, with focus in the editor, ALT+TAB away from your application, and then back again. Focus may not be set back into the editor. Here is a simple way to fix it. Put this code in an event or message handler that gets called when your application is re-activated:


IHTMLDocument2* pHTMLDoc2 = ...;
IHTMLWindow2* pWindow = 0;
pHtmlDoc2->get_parentWindow(&spWindow);
if (pWindow) {
  pWindow->focus();
  pWindow->Release();
}

Unselecting Current Selection

IHTMLTxtRange has a method to easily select a range of text, but there is no simple method to unselect an existing text selection. Here is a simple way to do it:


IHTMLDocument2* pHTMLDoc2 = ...;
IHTMLSelectionObject* pSelection = 0;
pHTMLDoc2->get_selection(&pSelection);
if (pSelection) {
  IDispatch* pDispRange = 0;
  pSelection->createRange(&pDispRange);

  IHTMLTxtRange* pTxtRange = 0;
  pDispRange->QueryInterface(IID_IHTMLTxtRange, (void**)&pTxtRange);
  if (pTxtRange) {
    VARIANT_BOOL bSuccess;
    pTxtRange->execCommand(CComBSTR(L"Unselect"), VARIANT_FALSE, CComVariant(), &bSuccess);
    pTxtRange->Release();
  }
  pDispRange->Release();
  pSelection->Release();
}