我有两个事件处理程序连接到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
}
我知道我可以取消订阅该事件,但是可以通过编程方式(从方法中)完成吗?
答案 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();
}
};