C# 防止调用下一个事件处理程序
我有两个事件处理程序连接到Windows窗体中的按钮单击,如下所示:C# 防止调用下一个事件处理程序,c#,.net,event-handling,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\u Click()?比如: v
this.BtnCreate.Click += new System.EventHandler(new RdlcCreator().FirstHandler);
this.BtnCreate.Click += new System.EventHandler(this.BtnCreate_Click);
两者都被正确调用
但是,在FirstHandler()
中是否有可能阻止执行BtnCreate\u Click()
?比如:
void FirstHandler(object sender, EventArgs e)
{
if (ConditionSatisfied)
//Prevent next handler in sequence being executed
}
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;
}
};
}
}
我知道我可以取消订阅活动,但这可以通过编程方式(从方法内部)完成吗?据我所知,没有解决方案。这是因为无法保证事件发生时调用事件处理程序的顺序
因此,您不应该以任何方式依赖它们的顺序。在此添加以下条件。btn创建\u单击这是第二个事件
BtnCreate_Click(object sender, EventArgs e)
{
if (!ConditionSatisfied) //Prevent next handler in sequence being executed
{
// your implementation goes here
}
}
为什么不用一个eventhandler替换它们呢?大概是这样的:
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的签名更改为:
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);
我建议您创建一个类包装器。因此,您可以在那里存储某种类型的事件标志组(例如,16位整数)和一些设置或取消设置单个位的方法(其中每个方法都表示调用或不调用特定的
事件处理程序)。您可以轻松地将事件处理程序的任何计数
甚至操作
存储在类中,并按所需的任何顺序调用。系统。ComponentModel
命名空间包含用于此目的的委托。它提供的参数之一是一个实例,该实例包含一个布尔Cancel
属性,可以将该属性设置为任何处理程序,以指示调用列表的执行应该停止
但是,要将其附加到普通的EventHandler
委托,您需要创建自己的包装器,例如:
void FirstHandler(object sender, EventArgs e)
{
if (ConditionSatisfied)
//Prevent next handler in sequence being executed
}
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
/* ... */
);
当然,如果以后需要取消订阅(分离)创建的链处理程序,则需要在字段中捕获该处理程序。正在寻找同一问题的解决方案,但运气不佳。所以我必须自己解决。
可取消事件参数的基类
public class CancelableEventArgs
{
public bool Cancelled { get; set; }
public void CancelFutherProcessing()
{
Cancelled = true;
}
}
接下来定义EventHandler的扩展方法,注意调用列表订阅者是按向后顺序调用的(在我的例子中,UI元素可以是添加到组件中的事件,因此稍后呈现的元素具有最高的可视性和优先级)
因此,如果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();
}
};
为什么不使用布尔标志呢?ConditionSuccessed通过ChainEventArgs模拟oneReplace EventArgs,并向其中添加一些可以支持处理程序的变量。chain LogicHandler会按照附加的顺序调用,除非故意以奇怪的方式实现添加/删除访问器。为什么不给他们打电话?它们存储在一个简单的列表中。@Groo虽然所有处理程序都是按其附加的顺序调用的,但这是一个不应该依赖的实现细节。我同意,不能依赖顺序,我会找到另一个方法。实现细节?根据MSDN:。我希望看到一个具体的例子,其中有人覆盖了add
访问器,并对列表进行了洗牌,以混淆他们的API用户。如果你真的想变得多疑,谁能保证我的访问者不会是这样:add{}
?你没有理解我的意思,伙计。您正在CreateFrom EventHandler中使用此条件,但我已将其放在BtnCreate\u Click EventHandler中。不同之处在于,您不需要取消订阅活动。这是唯一正统的方法。
game.OnMouseLeftButtonDown += (sender, a) =>
{
var xy = GetChessboardPositionByScreenPosition(a.XY);
if (IsInside(xy))
{
var args = new ChessboardEventArgs { Position = xy };
OnMouseDown.SafeInvokeWithCancel(this, args);
a.CancelFutherProcessing();
}
};