Just to verify . . . on wiring event handlers in CAB

Topics: CAB & Smart Client Software Factory
Aug 17, 2007 at 7:53 PM
With CAB, I can programmatically publish to an existing event (such as one defined in the Designer based on a control I didn't write) if it is of type EventHandler.

However, I can't publish to it if it is different, such as FormClosingEventHandler? I assume this inherits directly from MultiCastDelegate instead of from EventHandler? Or is it just the fact that it has a unique signature with its custom FormClosingEventArgs that makes it "not assignable" to EventHandler?

I just want to verify that I will always have to use a custom wrapper event around such existing handlers that are not simple EventHandlers.

When I write my own, of course, it's no problem because I can just use EventHandler or EventHandler<T>.

Thanks.
Aug 17, 2007 at 10:49 PM
This is not a CAB question (just to clarify). This is an Event/Delegate question and really has more to do with .NET than anything CAB specific.

FormClosingEventHandler is a delegate. Delegates are classes that hold references to methods. Methods have a specific signature. When you publish an event, your event has to match the signature of the method that will handle it on the receiving end. So when you create an event like so:

public event EventHandler MyEvent;

What you're saying is, I'm declaring an event (MyEvent) and I expect the receiving method to have a signature that matches this: (object sender, EventArgs e) which is the type of delegate.

You can declare any event you want to and make use of any delegate you wish. You could declare an event like this:

public event FormClosingEventHandler MyClosingEvent;

The FormClosingEventHandler expects a signature like this: (object sender, FormClosingEventArgs e).

That means a method matching that signature has to catch that event. To use that event you would then fire it like so:

if(MyClosingEvent != null)
    MyClosingEvent(this, new FormClosingEventArgs(CloseReason.None, true));


Or is it just the fact that it has a unique signature with its custom FormClosingEventArgs that makes it "not assignable" to EventHandler?


That is the basics, yes. An event is declared for a specific type of delegate. This is the type-safety of .NET events. Essentially, when you declare an event you must also declare its delegate type, which determines the signature of the method that is a valid responder. Any method that doesn't match will be caught by the compiler when you try and assign it.

A subscriber to a FormClosingEventHandler delegate must have the proper signature, namely it must have an (object sender, FormCloseEventArgs e) signature.

The short of it is: you can publish any kind of event you like, and you can listen for any kind of event you like. All you have to do is make sure you have the right delegate (method signature) and then somewhere in the code the publishers and subscribers have to be wired-up.
Aug 17, 2007 at 11:57 PM
Thanks Chris for the detailed response!

I still have plenty of gaps in my knowledge of .NET, even of basics like events :-)

However, I get an error saying that my delegate is not compatible to EventHandler, when I run this line of code:

EventTopic eTopic = RootWorkItem.EventTopicsEventTopicNames.ShellFormClosing;
eTopic.AddPublication(Shell, "FormClosing", RootWorkItem, PublicationScope.Global);

I am concerned that I can't pass in the type FormClosingEventArgs in the AddPublication() code, so it will be able to correctly build the signature for the method? Is this the issue? Or should the CAB code be able to discover the proper EventArgs class type via Reflection?

The code in CAB that yells at me is the following:
private void ThrowIfInvalidEventHandler(EventInfo info)
{
if (typeof(EventHandler).IsAssignableFrom(info.EventHandlerType) ||
(info.EventHandlerType.IsGenericType &&
typeof(EventHandler<>).IsAssignableFrom(info.EventHandlerType.GetGenericTypeDefinition())))
{
return;
}

throw new EventBrokerException(String.Format(CultureInfo.CurrentCulture,
Properties.Resources.InvalidPublicationSignature,
info.DeclaringType.FullName, info.Name));
}

Thanks again!
Aug 18, 2007 at 3:56 AM
I'll be honest - I have absolutely no idea what this code does:

EventTopic eTopic = RootWorkItem.EventTopics[EventTopicNames.ShellFormClosing];
eTopic.AddPublication(Shell, "FormClosing", RootWorkItem, PublicationScope.Global);

I've never used the EventBroker system that way, so I am not sure what exactly you're trying to achieve. I can look at it Monday when I'm near the CAB source, but as of right now I'm clueless :)
Aug 23, 2007 at 5:01 PM
CAB provides the ability to dynamically add multiple publishers to an Event Topic as well as the typical multiple subscribers, as well as the ability to fire an Event Topic at will. It is amazingly flexible.

My code shows adding a publisher: eTopic.AddPublication()

This is great. In particular, if you have an existing control, like a Form, with Event Handlers in the designer section of the code, then you can't decorate these Event Handlers with the EventPublication attribute. However, with the method AddPublication() you can do so, defining the name of hte existing EventHandler in the second parameter.

What I was attempting to verify, was that it's impossible to AddPublication() to an EventHandler that has a custom signature, such as FormClosingEventHandler. I assume it would have been too difficult for the CAB developers to use reflection to identify custom signatures or the class type of the custom EventArgs. I am no expert on these things.

However, I wanted to verify, because if this is possible, and I was just missing something, I'd like to learn.

For now, the only answer I've seen is to create your own custom EventHandler, decorate it with the Publication attribute, and then have the custom EventHandler fire your EventHandler. This effectively provides a wrapper. The wrapper allows you to receive the event and publish it to your CAB subscribers. But this approach doesn't allow you the flexibility to use the CAB EventTopic.Fire() method to fire that Form event.

Cheers,
Bryan