如何定製對話方塊中的回車鍵

2021-04-02 07:48:02 字數 3680 閱讀 8394

本文例子**

基於對話方塊的程式中,每次按下回車鍵時,程式都退出。去掉按鈕的 bs_defpushbutton 屬性並重寫onok函式也沒用。那麼如何定製回車鍵的行為呢?這個問題很easy,但是要說明白,卻要費點時間。 

這個問題在windows的開發中由來已久,對於初學者來說,這是個惱人的問題,幸運的是,人們找到了多種解決這個問題的方案。本文將告訴你定製回車鍵行為的方法。 

如果你想要disable回車鍵,最簡單的方法是過載onok函式,這固然是個不壞的主意,但如果你過載onok,讓它什麼事情也不幹,那麼當使用者用滑鼠按下回車鍵想真正做些什麼的時候怎麼辦呢?你可以改變回車鍵的id,如:id_my_ok,並寫乙個呼叫enddialog的處理器,這個方法雖然也能行得通,但顯得有點不專業。 

另外一種方法是disable回車鍵的「預設」屬性。這也是本文開始所提出的方法,之所以沒有成功,是因為僅僅uncheck 回車鍵的 bs_defpushbutton 屬性是不夠的,你可以利用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,必須在高位種蟹禱谼c_hasdefid。

begin_message_map(cmydlg, cdialog)

on_message(dm_getdefid, ongetdefid)

...end_message_map()

lresult cmydlg::ongetdefid(wparam wp, lparam lp)

因為mfc沒有對應dm_getdefid的巨集,你必須使用通用的on_massage巨集。這樣使用者可以隨意按回車鍵,但什麼事都不會發生。上面的做法是解決了按回車鍵程式退出的問題。但是又產生了另外乙個問題:如果想要回車鍵做些事情怎麼辦呢?有一些人曾經問過如何將回車鍵對映到tab鍵,既按下回車鍵就象按下tab鍵一樣-也就是說輸入焦點移動到下乙個對話方塊控制。這需要做一些工作才行,但最簡單的方式是使用加速鍵。許多程式設計師試圖用onchar,我會對他們說:no,no,no! onchar是乙個低階趣味的東西,你應該想方設法避免它,更糟的還有wm_keydown,wm_keyup之類的東西。誰能處理這些東西?onchar可以用來限制允許輸入編輯框的字元,如:數字,字母等。如果想要將乙個鍵對映到乙個命令,加速鍵才是最好的方法。

在本文的例子為vk_return建立了乙個加速鍵,將它對映到命令id_my_enter,並寫乙個命令處理器來做你想做的事情。

begin_message_map(cmydlg, cdialog) 

on_command(id_my_enter, onmyenter)

......

end_message_map()

void cmydlg::onmyenter()

下圖是本文例子的對話方塊和**

,**中的nextintaborder是實際起作用的函式。它使用getnextdlgtabitem來獲得tab順序的下乙個控制焦點。

while (getmessage(...))
在這裡細節不是重要的,重要的是訊息並不到達程式的流程,你必須請求訊息。這是一種人為的非搶先式多工方法,這種方法通過每乙個任務精誠協作來仿造多工環境,隨著增加的功能越來越多,有人想到了加速鍵表的主意,這個表用來對映按鍵和命令ids。為了實現這個目的,他們發明了乙個叫translateaccelerator的函式。現在這個訊息幫浦變成了如下的樣子:

while (getmessage(...))  else  

}

// 簡化後的 cwinthread 

while (getmessage(...)) else

}

cwinthread::pretranslatemessage是個虛函式,在應用中,其預設的實現以相同的名字呼叫另乙個虛函式,cwnd::pretranslatemessage。因此,如果你需要在訊息迴圈中做些什麼的話-如解釋加速鍵-你只要過載pretranslatemessage即可。實際上,這就是cframewnd處理加速鍵的方法。

bool cframewnd::pretranslatemessage(msg* pmsg) 

}

cframewnd 從**獲得加速鍵表呢?當你載入框架時,cframewnd::loadframe用與文件模板相同的id(如idr_mainframe)查詢加速鍵表,並將他載入到m_hacceltable。所有的處理細節在mfc中都是自動的、隱蔽的,你不用去操心-僅對主框架而言,如果是對話方塊,則是另外一種情況。因為cdialog不是從cframewnd派生而來,所以不繼承任何有關加速鍵的內容。

不用擔心,我們可以模仿cframewnd的工作,很容易為對話方塊增加加速鍵的功能。第一步是載入加速鍵,載入加速鍵最好的地方是在對話方塊的oninitdialog函式中:

bool cmydlg::oninitdialog() 

在加速鍵表中,你可以使用任何id。這裡我使用的是對話方塊本身的id,(m_lpsztemplatename既可以是乙個串名,也可以是乙個makeintresource使用的整型id):

// 本文例子中的加速鍵(in dlgkeys.rc )

idd_mydialog accelerators discardable

begin

vk_return, id_my_enter, virtkey, noinvert

end

一旦你已經載入加速鍵,剩下的事情是過載pretranslatemessage函式:

bool cmydlg::pretranslatemessage(msg* pmsg) 

return cdialog::pretranslatemessage(pmsg);

}

之所以要檢查按鍵類的訊息(從wm_ keyfirst 到 wm_keylast)是為了提高速度。如果你知道不是乙個按鍵訊息,你就不用浪費時間去呼叫translateaccelerator。再說translateaccelerator是乙個虛函式,不用增加乙個訊息對映入口。僅僅寫這個函式就可以了。

綜上所述,mfc中為對話方塊新增加速鍵功能的方法就是:載入加速鍵和過載pretranslatemessage函式。也就是說,如果你決定使用加速鍵,不用去操心ongetdefid,而是將沒有命令處理器的id對映到vk_return。本文的例子**中封裝了乙個又加速鍵的新對話方塊類:cdlgwinaccelerators,它是乙個通用類。希望大家喜歡它。最後祝大夥程式設計愉快。

控制項中按回車鍵就關閉對話方塊問題怎麼解決?

在對話方塊標頭檔案中定義 virtual void onok virtual void oncancel 在.cpp檔案中 過載回車鍵 void cpmagentmanagedlg onok 過載退出鍵 void cpmagentmanagedlg oncancel 如果要是針對某乙個控制項的回車 ...

MFC對話方塊應用程式按下回車鍵或者ESC鍵自動關閉

原文已經找不到鏈結,因此選了 原創 侵刪 mfc對話方塊應用程式中,按下回車鍵或者esc鍵,對話方塊會自動關閉。當使用者按下enter鍵時,程式就會自動去查詢 輸入焦點 落在了哪乙個按鈕上,獲得焦點的按鈕的四周將被點線矩形框所包圍。如果所有按鈕都沒有獲得輸入焦點,windows就會自動去尋找程式或資...

MFC對話方塊程式遮蔽回車與ESC鍵

取消掉 確定 按鈕的default button屬性,按回車鍵程式依然會退出,即使把 確定 按鈕刪除掉,效果也是一樣。看了下 inside visual c 引用一下書中的原話 當使用者按下enter鍵時,windows就會自動去查詢 輸入焦點 落在了哪乙個按鈕上,獲得焦點的按鈕的四周將被點線矩形框...