I’ve been working on a session I’m doing at the Dutch Conference To the Max conference for using the Web Browser control in .Net. For the most part the process of using this control is pretty straight forward – you import the ActiveX control and fire away at the browser events.


If you want to work with the document you can also import the Microsoft.mshtml library which is a 7meg  monster that gives you access to the entire HTML object model and event system. For the most part it works pretty well so you can do something like this quite easily:

 

HTMLDocument Doc = this.Browser.Document as mshtml.HTMLDocument;

string DocBody = Doc.body.outerHTML;

 

You can drill into the various collections, pick up selections and modify and fire away at the document model just fine.

 

However, I’ve had major issues with the document events. It’s easy enough to capture the events of the document like this:

 

HTMLDocumentEvents2_Event DocEvents = this.Browser.Document as HTMLDocumentEvents2_Event;

if (DocEvents == null)

      return false;

 

DocEvents.oncontextmenu += new

              HTMLDocumentEvents2_oncontextmenuEventHandler(Browser_ContextMenu);

 

All of this event information and the constants come as part of mshtml and it works to hook up the events. However, it comes with a major side effect – when you hook an event of the document in this fashion the document itself stops responding to mouse clicks. All other operations stay live, you can still tab to different areas and other events such as status bar events fire just fine. But you cannot click for instance on one of the links (you can click the mouse on the link then press enter, but you can’t click with your mouse). Obviously this is hardly an acceptable solution.

 

The workaround for this bug is to create a separate Event object that handles the event and then forwards it to a custom handler directly, by passing the Web Browser control. This concept is based on the fact that all IE Document events are fired exactly same way using an IHTMLEventObj object that is passed to an event handling method. All events such as OnMouseDown, OnContextMenu, OnFocus etc. all receive such a parameter which contains information such as mouse status, screen position and a return value property that you can set.

 

With this knowledge it’s possible to create a generic event object that receives these events and allows you to hook up your own handlers. This is a little more work than the built-in handlers but it works and keeps your document alive.

 

The Event Handler class and delegate  look like this:

///

/// Generic HTML DOM Event method handler.

///

public delegate void DHTMLEvent(IHTMLEventObj e);

 

///

/// Generic Event handler for HTML DOM objects.

/// Handles a basic event object which receives an IHTMLEventObj which

/// applies to all document events raised.

///

public class DHTMLEventHandler

{

      public DHTMLEvent Handler;

      HTMLDocument Document;

 

      public DHTMLEventHandler(HTMLDocument doc)

      {

            this.Document=doc;

      }

      [DispId(0)]

      public void Call()

      {

            Handler(Document.parentWindow.@event);

      }

}

 

Remember that the Document reloads on every hit, so in order to use the above handler all the time you have to re-hook it everytime the page is navigated. Typically this means that these sort of handler are hooked up as part of the Browser’s DocumentComplete handler.

 

The following hooks up the OnContextMenu event of the Document:

 

// *** Always raise the ContextMenuClicked event

// *** Using Custom Event Object  -   No Mouse Lockups

HTMLDocument doc = this.Document as HTMLDocument ;

DHTMLEventHandler Handler = new DHTMLEventHandler( doc );

Handler.Handler += new DHTMLEvent(this.Browser_ContextMenuStandardEvent);

doc.oncontextmenu = Handler;

 

The handler method then looks like this:

 

private void Browser_ContextMenuStandardEvent(mshtml.IHTMLEventObj e)

{

      MessageBox.Show("Context Menu Action (Event Object) Hooked");

      e.returnValue  = false;

}

 

Note that you can use the exact same signature and hookup code for any event of the HTML Document and any child elements in this same fashion. So if you want to capture a focus event for a DIV region inside of the document all you have to do is hook up the Element’s Onfocus event and assign the EventHandler to it the same way.

 

I hope this helps somebody out, because it took me quite some time to figure out this little tidbit. Actually I got a bit of help. The base idea came from an e-Book:

 

Programming Microsoft Internet Exporer in C# (Nikit Zykov – ebook):

http://www.amazon.com/exec/obidos/ASIN/B00007FYZP/westwindtechn-20/102-6053556-6633703

 

This $20 download e-book is a good read if you plan on using the browser control although it shows a very specific way of dealing with the control even though there are a number of approaches available. I simplified the code somewhat to make it more self-contained to just that small snippet above. The e-Book goes on to create a Document wrapper class that provides a bunch of common functionality.

 

I’m working on an article for this stuff, and I'll get that out when I get a little more time to clean up my notes.