为什么KeyPress的消息框会在KeyDown之前显示?

edw*_*963 6 c# event-handling winforms

按下文本框中的键,KeyDown事件发生在KeyPress之前.

我使用了计数和消息框来查看会发生什么.

以下是我的代码:

int Ncount = 0;
private void Textbox_KeyDown(object sender, KeyEventArgs e)
{
    Ncount += 1;
    MessageBox.Show("KeyDown's Ncount : " + Ncount.ToString());
}

private void Textbox_KeyPress(object sender, KeyPressEventArgs e)
{
    Ncount += 1;
    MessageBox.Show("KeyPress's Ncount : " + Ncount.ToString());
}
Run Code Online (Sandbox Code Playgroud)

当按下某个键时,这将首先显示...

KeyPress's Ncount : 2
Run Code Online (Sandbox Code Playgroud)

......接下来是这样的:

KeyDown's Ncount : 1
Run Code Online (Sandbox Code Playgroud)

KeyDown消息框(带有NCount 1)不应该显示在KeyPress消息框之前(使用Ncount 2)吗?

Han*_*ant 3

简短版本:MessageBox.Show() 是臭名昭著的Application.DoEvents披着羊皮的狼。绝对比 DoEvents 更良性,但它不能解决像这样的重入问题。Debug如果要显示调试信息,请始终使用.NET 中的类。

更长的版本:要理解这种行为,您首先必须了解一些事实:当您收到 KeyDown 事件时,操作系统已经生成了 KeyPress 通知。它耐心地坐在消息队列中,等待您的应用程序恢复调度程序循环。这将是您收到的下一个活动。除非您设置e.Handled为 true,否则 Winforms 会查看消息队列并清除 KeyPress 通知,以便该事件不会触发。

下一个事实:为了使 MessageBox 成为模式,或者任何 ShowDialog() 调用,它本身需要运行一个调度程序循环。这确保了基本的事情仍然会发生,比如绘制窗口和 MessageBox 识别“确定”按钮单击以及用户被“叮”击中!当他点击消息框以外的任何其他内容时。

也许您现在可以将这些点连接起来,MessageBox 中的调度程序循环将在队列中看到 KeyPress 通知。并导致您的 KeyPress 事件处理程序运行。因此,您显示另一个消息框,它必然位于第一个消息框的顶部。

这里没有什么问题,副作用只是盒子的 Z 顺序不是您期望的。如果您设置 e.Handled = true 并期望它能够工作,您会得到更多戏剧性的结果。不会的。不可以。当您的 KeyDown 事件处理程序完成时它已经被处理。

对此没有简单的解决办法。但第一,不要使用它。始终使用 Debug 类来生成调试信息。MessageBox 有太多副作用。