事件一次只能有一个呼叫吗?

时间:2010-12-01 21:06:06

标签: c# events event-handling

说我有这样的事件:

SomeClass.SomeEvent += MyMethodToCall();

...

public void MyMethodToCall()
{
     if (CheckToSeeIfFormIsAlreadyShowing())
     {
         SomeForm someForm = new SomeForm();
         someForm.ShowDialog();
     }
     else
     {
         DoSomeStuff();
     }
}

如果用户与SomeForm的互动导致SomeEvent再次触发,那么在someForm仍然显示时会再次调用MyMethodToCall吗?

我的证据似乎表明它不会。

我的问题是为什么?事件又发生了。为什么MyMethodToCall不再被调用(然后最终调用DoSomeStuff()

我猜这是不可能的(仍然不确定为什么)。假设不可能,我可以在单独的线程(begininvoke)上显示对话框吗? (我似乎记得UI都需要在同一个线程上发生,所以我对尝试它犹豫不决)。

如果事件再次发生,我需要再次调用该事件(即使SomeForm对话框仍在显示。有关如何做到这一点的任何想法都会很棒!

注意:这显然是一个人为的例子,我试图将我的复杂代码压缩成一个示例,演示我正在使用的真正问题。 (意思是请不要攻击我的例子。)

5 个答案:

答案 0 :(得分:3)

技术上肯定是可行的。例如,如果Timer触发SomeEvent。不是你不会注意到的,对话窗口的堆栈是非常明显的。

ShowDialog()采取反措施来防止这种情况发生。它完全等同于DoEvents(),这种方法已经遭受了很多糟糕的说唱。但ShowDialog通过禁用该线程拥有的所有窗口来防止绝大多数的重新入侵。因此用户不能再做任何麻烦,比如再次点击启动对话框的按钮,产生其中两个。或关闭主窗口,让代码仍在运行但没有用户界面,这是DoEvents()的典型损失。

如果用户无法介入和更改程序流,则事件触发的可能性很小,您没有依赖它或者在测试时没有看到该行为。不要太担心它,你会注意到,你会听到它。并且用旗帜很容易修复。虽然例外是一个更好的主意。

答案 1 :(得分:3)

您的活动可以被召唤两次。

原因是,对ShowDialog的调用并没有真正阻止线程。 ShowDialog运行一个内部循环,从消息队列中抽取消息,以便仅在关闭对话框后返回。

这意味着,当ShowDialog尚未返回时,UI事件仍会被触发并在您的UI线程上处理。

因此,例如,如果您从某个UI事件的事件处理程序触发SomeClass.SomeEvent事件(例如,单击对话框上的按钮),则SomeClass.SomeEvent 的事件处理程序将被调用。

如果你打破调试器并查看堆栈跟踪,你将看到你的原始事件处理程序,调用ShowDialog,调用消息泵,调用一些UI事件处理程序,再次调用SomeClass.SomeEvent处理程序。

如果您的问题是第二次没有调用该事件,您能否提供有关如何触发事件的详细信息?您是否尝试在调用事件的行上设置断点以确保它实际执行了?

我希望这能回答你的问题。

答案 2 :(得分:1)

使用:

someForm.Show();

而不是ShowDialog(),事件将继续触发:)

答案 3 :(得分:1)

在后台线程中触发SomeEvent。在处理程序中,当您想要显示对话框时,您必须封送对UI线程的调用。即使对话框仍处于打开状态,DoSomeStuff也将在工作线程上执行。

答案 4 :(得分:1)

根据你上面的评论,这就是我认为发生的事情。

SomeClass.SomeEvent事件是从属于您的驱动程序的后台线程触发的。此后台线程可能设计为等待某个内部句柄或队列,然后在需要时触发事件。

你所做的是在这个后台线程上调用ShowDialog。这有效地阻止了该线程,因为在处理程序返回之前它将无法继续触发事件,这只会在对话框关闭后才会发生。

我建议的解决方案是避免在驱动程序的后台线程上显示UI,并尽可能缩短事件处理程序。基本上你需要做的就是在你的控件上调用BeginInvoke,以便从你的UI线程中调用ShowDialog。

所以代码就是这样:

SomeClass.SomeEvent += SomeEventHandler;
...
...
...
void SomeEventHandler()
{
    // I'm assuming this code is in a class derived from Form
    this.BeginInvoke(new MethodInvoker(HandleEventOnUIThread));
}

void HandlerEventOnUIThread()
{
    if (CheckToSeeIfFormIsAlreadyShowing())
    {
        SomeForm someForm = new SomeForm();
        someForm.ShowDialog();
    }
    else
    {
        DoSomeStuff();
    }
}

这样,SomeClass.SomeEvent的处理将立即返回,后台线程将能够在需要时再次触发事件。