Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / WinForms

Handling HTML Events from .NET, using C#

4.88/5 (24 votes)
2 May 2008CPOL3 min read 1   1.8K  
This article demonstrates a simple way to bind to any HTML document event from a .NET WinForms application or a BandObject or a Browser Helper Object.

HtmlEventsSmple

Preface

I am not a native English speaker, and I am not heading for the Pulitzer Prize of Journalism. So, if anyone fine this “article” a bit short, get patient. My purpose here is to provide the most useful information with not too much talking. Besides, my C# English is fine enough for the VS2005 compiler :-). I hope the readers of this article will get their answer to the subject they Googled for: "Handling HTML events in .NET".

Background

I was looking for a way to handle HTML events with the .NET EventHandler. While writing my Password Management Toolbar, I needed a way to hook to the “onsubmit” event of the HTML document form in order to check the login information.

Anyway, I looked for forums and threads that would give me a straight answer, but didn’t find any. So, that left me no choice but to go to the last resort: the Reflector (Luts Roeder’s .NET Reflector).

What did I look for? When working with the WebBrowser control, you get the HtmlElement wrapper class that allows you to bind to mouse events pretty easily (like the HtmlElement.Click event). But, I do not work with the WebBrowser control, BandObject (this is the toolbar object), or the Browser Helper Object (BHO) which only have access to the MSHTML DOM elements. Also, the managed wrapper do not provide an event for “onsubmit”, so even the WebBrowser control wouldn’t do for me. So, I started digging with the Reflector to see what the guys in Microsoft did to allow binding to mouse events. The main thing that I found was the HtmlToClrEventProxy class which provided the correct interface for the object needed by the IHTMLElement2.attachEvent method. Anyway, this class is internal, so I took most of it, refined it, and made a similar public version of it that allows .NET users to easily attach to any HTMLElement event.

Using the code

The best way is to download and try the sample first. There is only one class you need to add to your project, this is the HtmlEventProxy class. This class have a factory method Create() which will do all the binding. Then, when you want to detach from the event, simply call the Detach() method of the object. The “sender” object in the EventHandler is the proxy itself. In order to get the underlying HTMLElement, use the HtmlElement property. Here are a few of the code snippets from the sample:

Let's start from the .NET event handler of the onsubmit form event. It has the regular signature of the EventHandler. The code here also extracts the form element from the sender and shows its outerHTML string.

I chose to detach from the event once it is consumed, using the Detach() method.

C#
private void FormSubmitHabdler(object sender, EventArgs e)
{
    MessageBox.Show("form submitted");

    // show the outer html of the form
    IHTMLElement form = 
         ((HtmlEventProxy)sender).HTMLElement as IHTMLElement;
    MessageBox.Show("outer html:" + form.outerHTML);

    //detach the event from the element
    ((HtmlEventProxy)sender).Detach();
}

Now, for the code that binds the form onsubmit event to the handler:

C#
object form = webBrowser1.Document.Forms[0].DomElement ; 
HtmlEventProxy.Create("onsubmit", form, FormSubmitHabdler);
 
object button = webBrowser1.Document.GetElementById("button1").DomElement; 
HtmlEventProxy.Create("onclick", button, ButtonClickHabdler);

First, obtain the element object from the document. Then, create a proxy with the name of the event, the DOM element, and the .NET handler.

The “HtmlEventProxy” class

The class implements the IReflect interface; that is the interface that is needed by IHTMLElement2.attachEvent(string ev,object pDisp). The most important is the implementation of the IReflect.InvokeMember method. This method detects the call to the first method entry and executes the .NET handler.

I also implemented the IDisposable interface, so the event is detached when the proxy is disposed.

C#
public class HtmlEventProxy : IDisposable,IReflect 
{ 
 // Fields private EventHandler eventHandler; 
 private object sender; 
 private IReflect typeIReflectImplementation; 
 private IHTMLElement2 htmlElement = null; 
 private string eventName = null; 

 // private CTOR 
 private HtmlEventProxy(string eventName, IHTMLElement2 htmlElement, 
                        EventHandler eventHandler) 
 { 
   this.eventName = eventName; 
   this.htmlElement = htmlElement; 
   this.sender = this; 
   this.eventHandler = eventHandler;
   Type type = typeof(HtmlEventProxy); 
   this.typeIReflectImplementation = type; 
 }

 public static HtmlEventProxy Create(string eventName, 
               object htmlElement, EventHandler eventHandler) 
 {
   IHTMLElement2 elem = (IHTMLElement2)htmlElement; 
   HtmlEventProxy newProxy = new HtmlEventProxy(eventName,elem, eventHandler); 
   elem.attachEvent(eventName, newProxy); return newProxy; 
 }

 /// detach only once (thread safe) 
 /// 
 public void Detach() 
 { 
   lock (this)
   { 
     if (this.htmlElement != null) 
     {
       IHTMLElement2 elem = (IHTMLElement2)htmlElement; 
       elem.detachEvent(this.eventName, this);
       this.htmlElement = null; 
     } 
   } 
 } 

 /// HtmlElemet property 
 /// 
 public IHTMLElement2 HTMLElement 
 {
   get 
   { 
     return this.htmlElement; 
   } 
 } 

 #region IReflect

 ......

 object IReflect.InvokeMember(string name,BindingFlags invokeAttr, Binder binder, 
                 object target, object[] args, ParameterModifier[] modifiers, 
                 CultureInfo culture, string[] namedParameters) 
 {
   if (name == "[DISPID=0]") 
   { 
     if (this.eventHandler != null) 
     {
       this.eventHandler(this.sender, EventArgs.Empty); 
     } 
   }
   return null; 
 }

#endregion 
}

You can download the latest version of HtmlEventProxy.cs from here.

Enjoy!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)