Exposing Events from managed controls
Earlier we talked about adding properties and methods to a managed control so that the page hosting the control can interact directly with the managed control programmatically.
This time we will add events which is a little bit more difficult. To really understand how this works, you have to understand the COM event model (Eric Lippert started a series talking about how this works particularly as it relates to script languages here: http://blogs.msdn.com/ericlippert/archive/2005/09/09/463215.aspx).
At a very high level, COM events work by allowing someone who wants to listen to events to implement an interface where each method on the interface represents an event that could be fired. The implementation of this COM interface is then provided to the event source which can call any one of the provided methods when an event is "fired". .NET has built in support for a more flexible event model based on delegates, but it does provide support for COM-style events through the System.Runtime.InteropServices.ComSourceInterfacesAttribute.
To take advantage of this in a control in the browser, we just need to define a COM interface that contains the signature of each event that we want to expose. It would look something like this:
using System.Runtime.InteropServices;
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)]
public interface ISimpleEvents {
[DispId(1)]//Each event must have a unique DispId
void ButtonClicked();
}
The interface has a single method/event "ButtonClicked".
To get our to use this COM event, we just need to add the System.Runtime.InteropServices.ComSourceInterfacesAttribute.
I expanded on the control we had earlier to add a button to the control in the browser which fires a "ButtonClicked" event which is handled by script in the hosting page.
Host.html
<html>
<head><title>Simple control host</title></head>
<body>
<H1>Simple control host page</H1>
<object id=simpleControl width=200 height=200 classid="SimpleControl.dll#SimpleControl"></object>
<br>
<a href="javascript:simpleControl.SayHello();">Say Hello</a>
<br>
<input type=text id=colorName value="Green"><input type=button onclick="simpleControl.ThemedBackgroundColor=colorName.value">
<script language=JavaScript>
function simpleControl::ButtonClicked() {
alert("Managed button clicked");
}
</script>
</body>
</html>
SimpleControl.cs
using System;
using System.Drawing;
using System.Security;
using System.Security.Permissions;
using System.Windows.Forms;
using System.Runtime.InteropServices;
[ComSourceInterfaces(typeof(ISimpleEvents))]
public sealed class SimpleControl : Control {
private Button button1;
public SimpleControl() {
this.BackColor = Color.Green;
button1 = new Button();
button1.Text = "Click Me!";
button1.Width= 100;
button1.Click += new EventHandler(HandleButtonClick);
Controls.Add(button1);
}
private void HandleButtonClick(object sender, EventArgs e) {
try {
new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();
MethodInvoker h = ButtonClicked;
if ( null != h) {
ButtonClicked();
}
} catch (Exception ex) {
MessageBox.Show(ex.ToString());
}
}
public event MethodInvoker ButtonClicked;
public void SayHello() {
MessageBox.Show("Hello from Windows Forms");
}
public string ThemedBackgroundColor {
get { return ColorTranslator.ToHtml(this.BackColor); }
set { this.BackColor = ColorTranslator.FromHtml(value); }
}
}
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)]
public interface ISimpleEvents {
[DispId(1)]//Each event must have a unique DispId
void ButtonClicked();
}
NOTE: firing the ButtonClickedEvent requires UnmanagedCode permissions (essentially FullTrust) to execute. This is because you are causing script code to execute which is not part of the managed CAS system and therefore considered unmanaged.
In order to get this sample to work, you will need to "trust" the assembly hosting your managed control.
I did this for mine by adding my site to the "trusted sites zone" in internet explorer and with the command line:
caspol -m -ag 1.5 -url http://blogs.msdn.com/andrewdownum/* FullTrust