2013-04-18 80 views
3

我对表单上的控件有很大的了解,并且有一段时间我想暂时停止处理所有事件。通常我只是做这样的事,如果我不想处理某些事件:暂时停止提出或处理任何形式的事件?

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有更快的方法,而不必更新每个处理程序方法。

+3

禁用您的控件,以便用户*知道*您没有响应。将它们的Enabled属性设置为false。 –

+2

如果你必须处理过度的事件触发,我会首先考虑重新设计。代码中发生错误的地方。你不应该有这个。在确定问题的真正原因之前,做这种管道应该只是一种临时措施。 – Neolisk

回答

8

您必须创建自己的机制来做到这一点。虽然这不是太糟糕。考虑添加另一个抽象层。例如,一个名为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); 

或改变其拍摄您创建包含到FilteredEventHandler参考一个EventArgs子类。这是更好的方式来做到这一点

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 

现在被调用可以取消本身,因为它有到得到了在它的参数传递正确的FilteredEventHandler对象的引用RealEventHandler。

我最后的建议,虽然是没做任何这个! Neolisk将它钉在评论中。做这样复杂的事情是一个迹象,表明设计存在问题。对于任何未来需要维护这些代码的人来说(即使是你,令人惊讶的是)都很难找出涉及的非标准管道。

通常,当您订阅活动时,您只需执行一次并忘记它 - 特别是在GUI程序中。

+0

我唯一一次明确解除事件是如果一个事件处理程序要执行可能会再次引发同一事件的操作。 GUI是我在90%的时间内处理事件的地方 –

+0

现在,如果我要移除如下所示的处理程序:myControl.SomeEvent - = new FilteredEventHandler(),它是否可以在FilteredEventHandler的终结器中删除RealEventHandler?我没有很多经验写我自己的事件,所以我不知道这个的影响,或者如果EventHandlers以与.NET中其他类型相同的方式完成(我假设它们是) –

+0

@AlexanderMiles I'我已经更新了我的原始答案以解决这个特定的问题 –

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

这对我来说似乎不是很“暂时”。 – Nyerguds