防止调用下一个事件处理程序

时间:2012-06-14 10:52:31

标签: c# .net event-handling

我有两个事件处理程序连接到Windows窗体中的按钮单击,如下所示:

this.BtnCreate.Click += new System.EventHandler(new RdlcCreator().FirstHandler);
this.BtnCreate.Click += new System.EventHandler(this.BtnCreate_Click);

两者都被正确调用。

但是FirstHandler()可以阻止BtnCreate_Click()被执行吗?类似的东西:

void FirstHandler(object sender, EventArgs e)
{
   if (ConditionSatisfied)
     //Prevent next handler in sequence being executed

}

我知道我可以取消订阅该事件,但是可以通过编程方式(从方法中)完成吗?

6 个答案:

答案 0 :(得分:5)

据我所知,没有解决方法。这是因为无法保证在事件发生时调用事件处理程序的顺序。

因此你不应该以任何方式依赖他们的订单。

答案 1 :(得分:2)

为什么不用一个事件处理程序替换它们?像这样:

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);
    }
};

这样你也可以保证处理程序的顺序。或者,使用上面的实现,但更改FirstHandler的签名以返回指示条件的bool(在这种情况下,它不再需要具有事件的签名):

    if (!rdlc.FirstHandler(sender, e)) { 
        this.BtnCreate_Click(sender, e);
    }

编辑:或者,您只需将第二个处理程序传递给FirstHandler 将FirstHandler的签名更改为:

void FirstHandler(object sender, EventArgs e, EventHandler nextHandler) {
    if (ConditionSatisfied) {
        // do stuff
    }
    else if (nextHandler != null) {
        nextHandler(sender, e);
    }
}

然后:

this.BtnCreate.Click += 
    (s, e) => new RdlcCreator().Firsthandler(s, e, this.BtnCreate_Click);

答案 2 :(得分:2)

System.ComponentModel命名空间包含一个CancelEventHandler委托,用于此目的。它提供的一个参数是一个CancelEventArgs实例,它包含一个布尔Cancel属性,可以设置为任何处理程序,以表示应该停止执行调用列表。

但是,要将其附加到普通EventHandler委托,您需要创建自己的包装器,如:

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;
            }
        };
    }
}

对于您的示例,您可以像这样使用它:

this.BtnCreate.Click += CancellableEventChain.CreateFrom(
    new RdlcCreator().FirstHandler,
    this.BtnCreate_Click
    /* ... */
);

当然,如果您需要稍后取消订阅(分离),则需要在字段中捕获创建的链处理程序。

答案 3 :(得分:0)

在this.BtnCreate_Click中添加以下条件,这是第二个事件

BtnCreate_Click(object sender, EventArgs e)  
{     
   if (!ConditionSatisfied)       //Prevent next handler in sequence being executed 
   {
     // your implementation goes here
   }   
}  

答案 4 :(得分:0)

我建议你创建一种类包装器。因此,您可以存储某种事件标志组(例如16位整数)和一些设置或取消设置单个位的方法(其中每个方法都调用或不调用EventHandler )。您可以轻松地在班级中存储Eventhandlers甚至Actions的任何计数,并以您想要的任何顺序调用。

答案 5 :(得分:0)

找到同样问题的解决方案,但没有运气。所以不得不解决自己。 Cancelable事件的基类args

public class CancelableEventArgs
{
    public bool Cancelled { get; set; }
    public void CancelFutherProcessing()
    {
        Cancelled = true;
    }
}

接下来定义了EventHandler的扩展方法,请注意,调用列表订阅者以向后顺序调用(在我的情况下,UI元素将事件添加到组件中,因此后面呈现的元素具有最大的可见性和更高的优先级)< / p>

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;
                }
            }
        }
    }

这是用法

public class ChessboardEventArgs : CancelableEventArgs
{
    public Vector2 Position { get; set; }
}

因此,如果UI元素在事件上有某些行为,则会取消进一步的处理

        game.OnMouseLeftButtonDown += (sender, a) =>
        {
            var xy = GetChessboardPositionByScreenPosition(a.XY);
            if (IsInside(xy))
            {
                var args = new ChessboardEventArgs { Position = xy };
                OnMouseDown.SafeInvokeWithCancel(this, args);
                a.CancelFutherProcessing();
            }
        };