I have two event handlers wired up to a button click in a Windows form like so:
this.BtnCreate.Click += new System.EventHandler(new RdlcCreator().FirstHandler);
Why don't you just replace them with one eventhandler? Something like this:
var rdlc = new RdlcCreator();
this.BtnCreate.Click += (sender, e) => {
rdlc.FirstHandler(sender, e);
if (!rdlc.HasHandledStuff) { // <-- You would need some kind of flag
this.BtnCreate_Click(sender, e);
}
};
That way you can also guarantee the order of the handlers. Alternatively, use the above implementation, but change the signature of FirstHandler to return a bool indicating the condition (as in this case it doesn't really need to have the event's signature anymore):
if (!rdlc.FirstHandler(sender, e)) {
this.BtnCreate_Click(sender, e);
}
EDIT: OR, you just pass the second handler to FirstHandler.
Change the signature of FirstHandler to this:
void FirstHandler(object sender, EventArgs e, EventHandler nextHandler) {
if (ConditionSatisfied) {
// do stuff
}
else if (nextHandler != null) {
nextHandler(sender, e);
}
}
and then:
this.BtnCreate.Click +=
(s, e) => new RdlcCreator().Firsthandler(s, e, this.BtnCreate_Click);
As far as I know there is no solution for this. That's because there is no guarantee for the order in which the event handlers are called when the event happens.
Because of that you are not supposed to rely on their order in any way.
Add the following condition in this.BtnCreate_Click which is the the second event
BtnCreate_Click(object sender, EventArgs e)
{
if (!ConditionSatisfied) //Prevent next handler in sequence being executed
{
// your implementation goes here
}
}
System.ComponentModel
namespace contains a CancelEventHandler delegate which is used for this purpose. One of the arguments it provides is a CancelEventArgs instance which contains a boolean Cancel
property which can be set be any of the handlers to signal that execution of the invocation list should be stopped.
However, to attach it to a plain EventHandler
delegate, you will need to create your own wrapper, something like:
public static class CancellableEventChain
{
public static EventHandler CreateFrom(params CancelEventHandler[] chain)
{
return (sender, dummy) =>
{
var args = new CancelEventArgs(false);
foreach (var handler in chain)
{
handler(sender, args);
if (args.Cancel)
break;
}
};
}
}
For your example, you would use it like this:
this.BtnCreate.Click += CancellableEventChain.CreateFrom(
new RdlcCreator().FirstHandler,
this.BtnCreate_Click
/* ... */
);
Of course, you would need to capture the created chain handler in a field if you need to unsubscribe (detach) it later.
I suggest you to create a some kind of class wrapper. So, you could store there some kind of event flag group (16bit integer, for example) and a few methods to set or unset individual bits (where each means to invoke or not particular EventHandler
). You can easily store any count of the Eventhandlers
or even Actions
, in the class, and invoke in any order you want.
Was finding the solution to the same question, but no luck. So had to resolve myself. A base class for Cancelable event args
public class CancelableEventArgs
{
public bool Cancelled { get; set; }
public void CancelFutherProcessing()
{
Cancelled = true;
}
}
Next defines the extension method for the EventHandler, note that Invocation List subscribers invoked in backward order (in my case UI elements subscibe the event as they added to components, so which element is rendered later has most visiblility and more priority)
public static class CommonExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void SafeInvokeWithCancel<T>(this EventHandler<T> handler, object sender, T args) where T : CancelableEventArgs
{
if (handler != null)
{
foreach (var d in handler.GetInvocationList().Reverse())
{
d.DynamicInvoke(sender, args);
if (args.Cancelled)
{
break;
}
}
}
}
And here is the usage
public class ChessboardEventArgs : CancelableEventArgs
{
public Vector2 Position { get; set; }
}
So if an UI element has some behaviour on the event, it cancells futher processing
game.OnMouseLeftButtonDown += (sender, a) =>
{
var xy = GetChessboardPositionByScreenPosition(a.XY);
if (IsInside(xy))
{
var args = new ChessboardEventArgs { Position = xy };
OnMouseDown.SafeInvokeWithCancel(this, args);
a.CancelFutherProcessing();
}
};