暂时停止引发或处理表单事件

本文关键字:处理 表单 事件 | 更新日期: 2023-09-27 18:09:48

我在表单上有很多控件,并且有一个特定的时间,当我想暂时停止处理所有事件时。通常,如果我不想处理某些事件,我会这样做:

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是否有比更新每个处理程序方法更快的方法。

暂时停止引发或处理表单事件

您必须创建自己的机制来完成此操作。不过还不算太糟。考虑添加另一个抽象层。例如,一个名为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程序中。

您可以使用反射…

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);
                }
            }
        }
    }
}

您可以直接禁用放置所有这些控件的容器。例如,如果您将它们放在GroupBoxPanel中,只需使用:groupbox.Enabled = false;panel.Enabled = false;。还可以禁用表单From1.Enabled = false;并显示等待游标。您仍然可以将这些控件复制并粘贴到表单以外的容器中。