说我有这样的事件:
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
对话框仍在显示。有关如何做到这一点的任何想法都会很棒!
注意:这显然是一个人为的例子,我试图将我的复杂代码压缩成一个示例,演示我正在使用的真正问题。 (意思是请不要攻击我的例子。)
答案 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的处理将立即返回,后台线程将能够在需要时再次触发事件。