我对表单上的控件有很多,并且有一段特定的时间我希望暂停所有事件的处理。如果我不想处理某些事件,通常我会做这样的事情:
private bool myOpRunning = false;
private void OpFunction()
{
myOpRunning = true;
// do stuff
myOpRunning = false;
}
private void someHandler(object sender, EventArgs e)
{
if (myOpRunning) return;
// otherwise, do things
}
但是我需要更新很多处理程序。只是好奇.NET是否比更新每个处理程序方法更快。
答案 0 :(得分:9)
您必须创建自己的机制才能执行此操作。但这并不算太糟糕。考虑添加另一层抽象。例如,一个名为FilteredEventHandler
的简单类,用于检查myOpRunning的状态,并调用实际事件处理程序或抑制事件。该课程看起来像这样:
public sealed class FilteredEventHandler
{
private readonly Func<bool> supressEvent;
private readonly EventHandler realEvent;
public FilteredEventHandler(Func<bool> supressEvent, EventHandler eventToRaise)
{
this.supressEvent = supressEvent;
this.realEvent = eventToRaise;
}
//Checks the "supress" flag and either call the real event handler, or skip it
public void FakeEventHandler(object sender, EventArgs e)
{
if (!this.supressEvent())
{
this.realEvent(sender, e);
}
}
}
然后当您挂钩事件时,请执行以下操作:
this.Control.WhateverEvent += new FilteredEventHandler(() => myOpRunning, RealEventHandler).FakeEventHandler;
当WhateverEvent
被提升时,它会调用FilteredEventHandler.FakeEventHandler method
。该方法将检查标志并调用或不调用真实事件处理程序。这在逻辑上和你正在做的一样,但是检查myOpRunning标志的代码只在一个地方而不是遍布你的代码。
编辑以在评论中回答问题:
现在,这个例子有点不完整。完全取消订阅该事件有点困难,因为您丢失了对连接的FilteredEventHandler的引用。例如,您不能这样做:
this.Control.WhateverEvent += new FilteredEventHandler(() => myOpRunning, RealEventHandler).FakeEventHandler;
//Some other stuff. . .
this.Control.WhateverEvent -= new FilteredEventHandler(() => myOpRunning, RealEventHandler).FakeEventHandler; //Not gonna work!
因为你正在联系一名代表并解开一个完全不同的代表!当然,两个委托都是FakeEventHandler方法,但这是一个实例方法,它们属于两个完全不同的FilteredEventHandler对象。
不知何故,您需要获取对您构造的第一个FilteredEventHandler的引用以便取消挂钩。这样的东西可以工作,但它涉及跟踪一堆FilteredEventHandler对象,这可能不比你想要解决的原始问题好:
FilteredEventHandler filter1 = new FilteredEventHandler(() => myOpRunning, RealEventHandler);
this.Control.WhateverEvent += filter1.FakeEventHandler;
//Code that does other stuff. . .
this.Control.WhateverEvent -= filter1.FakeEventHandler;
在这种情况下,我要做的是让FilteredEventHandler.FakeEventHandler方法将其“this”引用传递给RealEventHandler。这涉及将RealEventHandler的签名更改为另一个参数:
public void RealEventHandler(object sender, EventArgs e, FilteredEventHandler filter);
或更改它以获取您创建的EventArgs子类,该子类包含对FilteredEventHandler的引用。这是更好的方法
public void RealEventHandler(object sender, FilteredEventArgs e);
//Also change the signature of the FilteredEventHandler constructor:
public FilteredEventHandler(Func<bool> supressEvent, EventHandler<FilteredEventArgs> eventToRaise)
{
//. . .
}
//Finally, change the FakeEventHandler method to call the real event and pass a reference to itself
this.realEvent(sender, new FilteredEventArgs(e, this)); //Pass the original event args + a reference to this specific FilteredEventHandler
现在被调用的RealEventHandler可以取消订阅,因为它引用了传递给其参数的正确FilteredEventHandler对象。
我的最后建议是,不做任何此事! Neolisk在评论中将其钉住了。做这样复杂的事情表明设计存在问题。对于任何需要在未来维护此代码的人(即使您,令人惊讶!)来说,要弄清楚所涉及的非标准管道将是困难的。
通常当你订阅活动时,你会做一次而忘记它 - 特别是在GUI程序中。
答案 1 :(得分:1)
你可以用反射来做到这一点......
public static void UnregisterAllEvents(object objectWithEvents)
{
Type theType = objectWithEvents.GetType();
//Even though the events are public, the FieldInfo associated with them is private
foreach (System.Reflection.FieldInfo field in theType.GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance))
{
//eventInfo will be null if this is a normal field and not an event.
System.Reflection.EventInfo eventInfo = theType.GetEvent(field.Name);
if (eventInfo != null)
{
MulticastDelegate multicastDelegate = field.GetValue(objectWithEvents) as MulticastDelegate;
if (multicastDelegate != null)
{
foreach (Delegate _delegate in multicastDelegate.GetInvocationList())
{
eventInfo.RemoveEventHandler(objectWithEvents, _delegate);
}
}
}
}
}
答案 2 :(得分:0)
您可以仅禁用放置所有这些控件的容器。例如,如果将它们放置在GroupBox
或Panel
中,则只需使用:groupbox.Enabled = false;
或{{1} }。您也可以禁用表单panel.Enabled = false;
并显示一个等待光标。您仍然可以将这些控件复制并粘贴到表单以外的其他容器中。