我有两个事件处理程序连接到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
}
我知道我可以取消订阅事件,但这可以通过编程方式完成(从方法中)吗?
据我所知,没有解决方案。这是因为无法保证事件发生时调用事件处理程序的顺序。
因此,您不应该以任何方式依赖他们的订单。
为什么不将它们替换为一个事件处理程序?像这样:
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 的签名以返回指示条件的布尔值(因为在这种情况下,它实际上不再需要具有事件的签名):
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);
System.ComponentModel
命名空间包含一个用于此目的的CancelEventHandler
委托。它提供的参数之一是包含布尔Cancel
属性的CancelEventArgs
实例,该属性可以设置为任何处理程序,以指示应停止执行调用列表。
但是,要将其附加到普通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
/* ... */
);
当然,如果您稍后需要取消订阅(分离)它,则需要在字段中捕获创建的链处理程序。
在此添加以下条件。BtnCreate_Click这是第二个事件
BtnCreate_Click(object sender, EventArgs e)
{
if (!ConditionSatisfied) //Prevent next handler in sequence being executed
{
// your implementation goes here
}
}
我建议你创建一个类包装器。因此,您可以在那里存储某种事件标志组(例如 16 位整数)和一些设置或取消设置单个位的方法(其中每个表示调用或不特定EventHandler
)。您可以轻松地在类中存储Eventhandlers
甚至Actions
的任何计数,并按所需的任何顺序进行调用。
找到了同一问题的解决方案,但没有运气。所以必须自己解决。可取消事件参数的基类
public class CancelableEventArgs
{
public bool Cancelled { get; set; }
public void CancelFutherProcessing()
{
Cancelled = true;
}
}
接下来定义 EventHandler 的扩展方法,请注意,调用列表订阅者以向后顺序调用(在我的例子中,UI 元素在添加到组件时会订阅事件,因此稍后呈现哪个元素具有最明显的优先级)
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();
}
};