基於對話框的程序中,每當用戶按下回車鍵時,程序都會退出,其效果和按下對話框中的默認"OK"按鈕是一樣的,即使去掉"OK"按鈕的 BS_DEFPUSHBUTTON 屬性也沒用。那麼如何定制回車鍵的行為呢?這個問題在Windows的開發中由來已久,對於初學者來說,這是個惱人的問題,幸運的是,人們找到了很多種解決這個問題的方案。本實例將告訴你實現定制回車鍵行為的方法。
一、實現方法
如果想要使回車鍵無效,最簡單的方法是重載OnOK()函數,這固然是個不壞的主意,但如果重載OnOK()函數,讓它什麼事情也不干,那麼當用戶用鼠標按下"OK"按鈕想真正做些什麼的時候怎麼辦呢?你可以改變回車鍵的ID,如:ID_MY_OK,並寫一個調用EndDialog()的處理器,這個方法雖然也能行得通,但顯得有點不專業。
另外一種方法是"disable"回車鍵的"默認"屬性。這也是本文開始所提出的方法,之所以沒有成功,是因為僅僅不設置"OK"按鈕的BS_DEFPUSHBUTTON 屬性是不夠的,可以利用Visual C++中的Spy++工具仔細地觀察,就能發現回車鍵仍然我行我素發送退出消息。問題出在哪呢?你必須區分OK按鈕和回車鍵,你可以寫一個OnOK處理器調用GetCurrentMessage()函數獲取最後發送的消息,應該是WM_COMMAND,再檢查WPARAM的低位字(low-order Word)看看命令來自何處。
要解決問題,必須搞清楚背後所發生的一切,在Spy++中可以看到,當用戶按下回車鍵時,Windows發送一個特殊的WM_GETDEFID消息來獲得缺省的命令ID,Windows再將它作為WM_COMMAND發送。所以,我們要做的就是重載WM_GETDEFID消息。在有關Windows的文檔中是這樣描述WM_GETDEFID返回值的:"如果有缺省的按鈕,則返回值的高位字包含DC_HASDEFID,低位字包含控制的標識符。否則,返回值是零"。根據這段描述,假設如果沒有缺省得按鈕,則返回值應該是零。如果想要disable缺省得ID,必須在高位字中返回DC_HASDEFID,為此定義和實現消息映射函數如下:
BEGIN_MESSAGE_MAP(CMyDlg, CDialog)
ON_MESSAGE(DM_GETDEFID, OnGetDefID)
...
END_MESSAGE_MAP()
LRESULT CMyDlg::OnGetDefID(WPARAM wp, LPARAM lp)
{
return MAKELONG(0,DC_HASDEFID);
}
因為MFC沒有對應DM_GETDEFID的宏,你必須使用通用的ON_MASSAGE宏。這樣用戶可以隨意按回車鍵,但什麼事都不會發生。
上面的做法是解決了按回車鍵程序退出的問題,但是又產生了另外一個問題:如果想要回車鍵做些事情怎麼辦呢?有一些人曾經問過如何將回車鍵映射到TAB鍵,既按下回車鍵就象按下TAB鍵一樣,也就是說輸入焦點移動到下一個對話框控制。這需要做一些工作才行,但最簡單的方式是使用加速鍵。許多程序員試圖用OnChar()響應函數,但它是一個低級趣味的東西,應該想方設法盡量避免使用它,更糟的還有WM_KEYDOWN,WM_KEYUP之類的消息。誰能處理這些消息呢?OnChar()可以用來限制允許輸入編輯框的字符,如:數字,字母等。如果想要將一個鍵映射到一個命令,加速鍵才是最好的方法。
在本實例中為VK_RETURN創建了一個加速鍵,將它映射到命令ID_MY_ENTER,並寫一個命令處理器來實現任何想實現的事情。
如果你細心的話會發現另外一個還沒有得到解決的問題,那就是在MFC對話框不自動處理加速鍵,你必須自己編寫代碼來做這件事情。為了理解弄清楚這是為什麼,讓我們回首Windows開發的歷程,在使用C和原始的Windows API的年代,每一個Windows程序中都有一個叫做消息泵的中樞循環:
while (GetMessage(...)) {
TranslateMessage(...);
DispatchMessage(...);
}
在這裡細節不是最重要的,最重要的是消息並不到達程序的流程,你必須請求消息。這是一種人為的非搶先式多任務方法,這種方法通過每一個任務精誠協作來仿造多任務環境,隨著增加的功能越來越多,有人想到了加速鍵表的主意,這個表用來映射按鍵和命令IDs。為了實現這個目的,微軟發明了一個叫TranslateAccelerator()的函數。現在這個消息泵變成了如下的樣子:
while (GetMessage(...)) {
if (TranslateAccelerator(hAccel...)) {
// handled, continue looping
} else {
TranslateMessage(...);
DispatchMessage(...);
}
}
hAccel是個加速鍵表句柄,在這裡細節同樣不是重要的,重要的是如何利用加速鍵表,也就是要有一個專門的函數將按鍵消息解釋為WM_COMMAND消息。TranslateAccelerator()函數尋找WM_KEYDOWN,WM_CHAR,WM_KEYUP序列與表中鍵值匹配的字符。如果找到,它插入一條WM_COMMAND到消息隊列,在消息隊列中的命令ID可以是加速鍵表定義的任何入口。這樣你只要設置加速鍵表(在資源中)並記住調用對應的函數TranslateAccelerator(),就什麼都不用擔心了。
隨著Visual C++和MFC的日臻成熟,現在幾乎整個消息循環(但不是全部)都被隱藏到了MF
[1] [2] [3] [4] 下一頁