2015-08-15 46 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

... 但在相反的/預期的順序框顯示該消息中的時間戳真的