2015-08-15 72 views
5

在文本框中按下键时,KeyDown事件发生在KeyPress之前。为什么KeyPress的消息框在KeyDown之前显示?

我用一个计数和消息框来看看会发生什么。

以下是我的代码:

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

当按下一个键,这首先会显示...

KeyPress's Ncount : 2 

...其次是这样的:

KeyDown's Ncount : 1 

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

+4

我想所有的MessageBox.Show调用都在一个堆栈中,并且在Windows准备就绪后立即调用。这意味着第一个窗口位于堆栈的底部,第二个窗口首先弹出。这没有得到证实,只是一个猜测。 –

+0

使用Ncount只是为了确保KeyDown在KeyPress之前发生。 Ncount被初始化为零。 – edwhere0963

+0

嗯,我只是简单地把一个断点,看看哪一个被调用第一个 –

回答

2

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

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

下一个factoid:为了使MessageBox变为模态,或者对于任何ShowDialog()调用,它需要运行一个调度器循环本身。这确保了基本的东西仍然发生,如绘制窗口和MessageBox识别OK按钮点击并且用户用丁打耳光!当他点击消息框以外的其他任何东西。

也许你现在可以连接点,MessageBox中的调度器循环将在队列中看到KeyPress通知。并使您的KeyPress事件处理程序运行。所以你会显示另一个消息框,它必须位于第一个消息框的顶部。

什么也没有显着这里是错误的,副作用只是框的Z顺序不是你所期望的。如果你设置e.Handled = true并且期望它能够工作,你会得到更多的戏剧性。它不会。它不能。它已在您的KeyDown事件处理程序完成时处理完毕。

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

1

KeyPressEventArgs指定用户按下某个键时组成的字符。例如,当用户按下SHIFT + K时,KeyChar属性返回大写K.

当用户按下某个键时会发生KeyPress事件。与KeyPress事件密切相关的两个事件是KeyUp和KeyDown。当用户按下按键时,KeyDown事件在每个KeyPress事件之前,并且当用户释放按键时发生KeyUp事件。当用户按住某个键时,每次重复字符时都会发生重复KeyDown和KeyPress事件。一个KeyUp事件在发布时生成。

对于每个KeyPress事件,传递一个KeyPressEventArgs。每个KeyDown和KeyUp事件都会传递一个KeyEventArgs。 KeyEventArgs指定是否有任何修饰键(CTRL,SHIFT或ALT)与另一个键一起按下。 (此修饰符信息也可以通过Control类的ModifierKeys属性获得。)

将Handled设置为true以取消KeyPress事件。这样,可以避免处理按键控制“

但注意 -

”。有些控件将处理上的KeyDown某些击键。例如,RichTextBox在调用KeyPress之前处理Enter键。在这种情况下,你无法取消KeyPress事件,并且必须从的KeyDown取消击键而不是“

文档显然的KeyDown按键响应之前触发并可以显示通过编码是这样的,它的作用:。

Private Sub TextBox1_KeyDown(sender As Object, e As KeyEventArgs) _ 
    Handles TextBox1.KeyDown 
    Debug.Print("Down") 
    MessageBox.Show("Down") 
    e.Handled = True 
End Sub 

Private Sub TextBox1_KeyPress(sender As Object, e As KeyPressEventArgs) _ 
    Handles TextBox1.KeyPress 
    Debug.Print("Press") 
    MessageBox.Show("Press") 
End Sub 

两个处理器运行这一点,你会看到调试写“打倒”,“新闻”了。您也可以,但断点和看到的KeyDown火灾按键响应之前。

您还将看到“新闻“MessageBox显示在”Down“MessageBox之前,这很好奇,如果有人能解释它,我会感兴趣。

参考:KeyPress Event firing before KeyDown Event on textbox

+0

虽然这可能会回答问题,如果该链接永远不会死你的答案变得毫无用处。最好总结链接中的信息,这样一个人就可以获得足够的信息来获取他们需要的信息。 –

+0

我认为你应该编辑它。 –

1

thread on MSDN forums讲此古怪:

的事件处理程序在一个单独的线程中运行。模态形式在自己的线程环境中为 模态。

因此,虽然消息框在窗体的UI线程中是模态的,但事件处理程序中显示的消息框并非如此。

消息框确实以预期顺序显示;他们只是不是上面的注释模式:所以他们出现是在相反的顺序(当第二个/ KeyPress消息框顶部并覆盖第一个/ KeyDown消息框)。

的示例代码变种证明了我的意思:

int Ncount = 0; 

private void textBox1_KeyDown(object sender, KeyEventArgs e) 
{ 
    Ncount += 1; 
    var message = 
     String.Format(
      "({0}) KeyDown's Ncount : {1}", 
      DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.fff", CultureInfo.InvariantCulture), 
      Ncount); 
    Debug.WriteLine(message); 
    MessageBox.Show(message); 
} 

private void textBox1_KeyPress(object sender, KeyPressEventArgs e) 
{ 
    Ncount += 1; 
    var message = 
     String.Format(
      "({0}) KeyPress's Ncount : {1}", 
      DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.fff", CultureInfo.InvariantCulture), 
      Ncount); 
    Debug.WriteLine(message); 
    MessageBox.Show(message); 
} 

它产生以下控制台输出...

(2015-08-15 03:45:31.455) KeyDown's Ncount : 1 
(2015-08-15 03:45:31.487) KeyPress's Ncount : 2 

...和消息框在以下意外顺序它似乎 ...

KeyPress Message Box

KeyDown Message Box

... 但在相反的/预期的顺序框显示该消息中的时间戳真的