轉自:http://soniabubu.blog.163.com/blog/static/59006220201171653544686/
UPDATE_COMMAND_UI機制對於動態的把 通過檢查 或通過激活或失效 菜單命令和工具欄按鈕 而發生變化的應用程序的狀態 反饋給用戶是非常重要的。不幸的是,ClassWizard並不支持直接爲對話框加入UPDATE_COMMAND_UI句柄,然而通過努力還是能夠利用 UPDATE_COMMAND_UI機制來動態的更新對話框的。
第一步:手工爲對話框類增加一個WM_KICKIDLE私有的MFC消息句柄,實現這些,遵循以下步驟:
1a、包含#include <afxpriv.h> ,該頭文件裏定義了這個消息
1b、在對話框類頭文件里加入OnKickIdle()函數原型。
afx_msg void OnKickIdle();
1c、增加該消息的消息映射宏
ON_MESSAGE_VOID(WM_KICKIDLE , OnKickIdle)
1d、實現該函數
void CDemoDlg::OnKickIdle()
{
UpdateDialogControls(this,false);
}
第二步:爲每一個需要檢查的控件加入UPDATE_COMMAND_UI消息句柄,通過pCmdUI來完成其工作。
2a、在對話框類頭文件里加入OnUpdateEmpty()函數原型。
afx_msg void OnUpdateEmpty(CCmdUI* pCmdUI);
2b、增加該消息的消息映射宏
ON_UPDATE_COMMAND_UI(IDC_EMPTY , OnUpdateEmpty)
2c、實現該函數
void CDemoDlg::OnUpdateEmpty(CCmdUI* pCmdUI)
{
pCmdUI->Enable(m_edtEdit.GetWindowTextLength()>0);
}
WM_IDLEUPDATECMDUI,WM_KICKIDLE消息___基於對話框的應用中執行空閒狀態處理
#define WM_IDLEUPDATECMDUI 0x0363 消息處理ui得更新。
.net對應得是Applicaton得onidle事件
void XMainFrame::IdleUpdateCmdUI()
{
SendMessageToDescendants(WM_IDLEUPDATECMDUI, TRUE, 0);
}
如果工具條浮動,不是屬於framewnd得子窗體需要顯示調用
pToolBar->OnIdleUpdateCmdUI(0,0);
對於對話框使用WM_KICKIDLE消息
BEGIN_MESSAGE_MAP(CDlgZBBXiada, CDialog)
//{{AFX_MSG_MAP(CDlgZBBXiada)
ON_MESSAGE(WM_KICKIDLE, OnKickIdle)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
或者是
LRESULT CFormBuilderDlg::DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
// TODO: Add your specialized code here and/or call the base class
if(message == WM_KICKIDLE)
{
}
return CDialog::DefWindowProc(message, wParam, lParam);
}
可以使用在mainframe中一樣得ON_UPDATE_COMMAND_UI宏。可以更新對話框中得子控件,但是對對話框中得工具條按鈕不起作用。必須另外處理。
簡單如下
1.
#define WM_KICKIDLE 0x036A
LRESULT CTestDlg2Dlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
// TODO: Add your specialized code here and/or call the base class
if(message == WM_KICKIDLE)
{
UpdateDialogControls(this,true);
}
}
2.自己添加處理ON_UPDATE_COMMAND_UI宏就可以了。
June 1995,Microsoft System Journal
Paul DiLascia 是一個自由軟件顧問,專長是訓練和軟件開發(C++ and Windows).他是Windows ++: Writing Reusable Code in C++ (Addison-Wesley, 1992)的作者.
問:我的問題是OnIdle在通常的文檔/視圖程序中可以工作,但是看起來在基於對話框的程序中不行。我的CApp::InitInstance調用 dlg.DoModal,調用一個函數:不調用OnIdle的CWnd::RunModalLoop。我想我應該在WM_ENTERIDLE中做一些後臺 處理,但是這個消息是發送到對話框的父窗口的。在我的這種情況下,父窗口不存在。請幫忙!
Jim Kallimani
如你所見, “模態”對話框在MFC4.0中實際上是非模態的。當你調用CDialog::DoModal的時候,MFC並不調用::DialogBox,像它以前所 做的,而是調用CreateDialogIndirect (在三思之後)然後通過禁用父窗口並且進入自己的消息循環模擬模態行爲。這是::DialogBox所做的本質工作。這樣做的好處是MFC擁有對話框的消 息循環,以前它被Windows API函數::DialogBox隱藏起來了。這樣MFC通過通常MFC的消息泵CWinThread::PumpMessage取模態對話框消息,像其
他窗口一樣。特別的,你可以爲模態對話框重載CWnd::PreTranslateMessage—例如實現加速鍵。MFC的早期版本允許你爲模態對話框 實現你自己的PreTranslateMessage,但是沒有被系統調用,因爲CDialog::DoModal直接執行::DialogBox,直到 你的對話框消息處理函數調用EndDialog才返回。同樣,使用::DialogBox, 使用通常的MFC方法進行消息處理是不可能的,因爲程序控制消失在::DialogBox中,直到對話框結束才返回。
作爲替代方案, Windows有它自己的機制, WM_ENTERIDLE, 在模態對話框中進行消息處理。當處理完一個或多個消息之後,Windows 發送 WM_ ENTERIDLE 到一個模態對話框或菜單的所有者,如果消息隊列中沒有等待的消息的話。只有模態對話框發送WM_ENTERIDLE,而非模態對話框不發送。因爲MFC現 在使用非模態對話框,甚至是在使用模態對話框的時候實際上也是使用非模態對話框,MFC不得不自己發送WM_ENTERIDLE以模仿模態對話框—-但是 僅當對話框有父窗口的時候才這麼幹。Jim碰到麻煩,因爲沒有父窗口來接收WM_ENTERIDLE。你的頭快昏了嗎?
如果MFC通過標準消 息泵取模態對話框消息,爲什麼不調用CWinApp::OnIdle作爲自己消息處理的一部分?爲題是CWnd::RunModalLoop 調用了CWinThread::PumpMessage但是OnIdle在CWinThread::Run中出現。當你的應用程序調用了 InitInstance函數之後,MFC調用CWinThread::Run運行你的應用程序。CWinThread::Run的濃縮形式看起來像這 樣:
// (from THRDCORE.CPP)
int CWinThread::Run()
{
// 爲了空閒狀態處理
BOOL bIdle = TRUE;
LONG lIdleCount = 0;
for (;;) {
while (bIdle && !::PeekMessage(...)) {
//當在bIdle狀態時調用OnIdle
if (!OnIdle(lIdleCount++))
// 假設"非空閒" 狀態
bIdle = FALSE;
}
// 獲取/預處理/分派消息
// (調用 CWinThread::PumpMessage)
}
}
我砍掉了很多,以強調空閒處理如何工作。如果沒有消息在等待,MFC重複調用CWinThread::OnIdle,傳遞給它每次增加的一個計數器參 數。你可以使用這個參數區分不同種類的空閒處理的優先次序。你可能在空閒計數爲1時作格式化,空閒計數爲2時更新一個指示當天時間的時鐘。當你的 OnIdle返回FALSE時,MFC停止調用它並且等待,直到你的線程得到另一個消息,因此空閒循環從頭開始。
模態對話框從不執行這個代 碼,因爲CWnd::RunModalLoop直接在自己的消息循環中調用CWinThread::PumpMessage。它沒有調用 CWinThread::Run,因此從不調用CWinThread::OnIdle。 Redmond 的人員告訴我這是由設計上決定的。顯然,在模態對話框中調用OnIdle是危險的,因爲許多消息處理函數建立臨時CWnd對象,它們被期望在對話框生存期 中存在。默認空閒處理的一部分就是釋放臨時句柄映射。(譯者注:臨時CWnd對象依賴於臨時句柄映射而存在。.)
(我不得不告訴你,依我所見,整個MFC用來連接HWND和CWnd的臨時/永久句柄映射機制是整個架構中的災難之一,甚至比它們的消息映射還要壞。臨時映射機制的問題不斷出現在程序中—特別是在多線程應用中,使得他們很難用MFC編寫。)
這樣看來,你如何在基於對話框的應用程序中進行消息處理,當對話框沒有父窗口的時候?幸運的是,它易如反掌。MFC開發者提供一個鉤子: WM_KICKIDLE。 RunModalLoop 不斷髮送這個MFC私有消息,當消息隊列中沒有消息的時候—就像CWinThread::Run調用OnIdle一樣。 RunModalLoop甚至還爲你提供一個計數器並且依次遞增。實際上,WM_KICKIDLE是對話框的OnIdle替代品。 (歷史信息:早期版本的MFC爲屬性表作這個模態/非模態切換和提供WM_KICKIDLE。顯然它工作的如此之好,以至於他們決定使所有的模態對話框非
模態化。)
要警告你的是:你可能在OnKickIdle函數中,想調用你的主應用程序的OnIdle函數
LRESULT CMyDlg::OnKickIdle(WPARAM, LPARAM lCount)
{
return AfxGetApp()->OnIdle(lCount);
}
MFC人員告訴我這是危險的;因爲臨時映射問題。在OnKickIdle中執行你的空閒處理會更安全一些。如果有必要,你可以組合共有的空閒處理成爲一個輔助函數,在CApp::OnIdle 和 CMyDlg::OnKickIdle中調用。
當我在處理空閒處理的問題的時候,發現不是所有程序員都知道CDocTemplate和CDocument的OnIdle函數! 如果你要在文檔或文檔模板中執行空閒處理,只需重載這些函數。
Internet:
Paul DiLascia
From the June 1996 issue of Microsoft Systems Journal.
July 1997,Microsoft System Journal
......
下面我將指出如何使ON_COMMAND_UPDATE_UI處理函數在對話框中工作。在通常的MFC文檔/視圖應用中,MFC使用內部消息 WM_IDLEUPDATECMDUI更新菜單項、工具欄按鈕、狀態欄格等用戶界面對象。作爲空閒處理的一部分,IDLEUPDATECMDUI廣播到所 有你的應用程序的窗口。工具欄、狀態欄和對話欄的命令處理依次調用UpdateDialogControls廣播另一個命令, CN_UPDATE_COMMAND_UI,到窗口上的所有控件。從你的程序員的角度來看,這些消息是不可見的。你只需實現
ON_UPDATE_COMMAND_UI處理你的菜單項和按鈕,然後,看? 它們被變魔法似的更新了。(需要更多信息的話,參見我的在1995年6月MSJ.上的文章 "Meandering Through the Maze of MFC Message and Command Routing" )
不幸的是,這個奇妙的UI更新機制不能用於對話框—至少不是自動的。 你必須自己修補一下。幸好它很簡單。你只需處理WM_KICKIDLE,一個MFC私有消息;當對話框空閒時發送出來(類似應用程序的OnIdle處理) 給.你自己調用UpdateDialogControls。.
LRESULT CTabDialog::OnKickIdle(WPARAM wp,
LPARAM lCount)
{
UpdateDialogControls(this, TRUE);
return 0;
}
CWnd::UpdateDialogControls發送魔術般的CN_ UPDATE_COMMAND_UI 消息給所有對話框控件,結果是現在ON_COMMAND_ UPDATE_UI處理突然在對話框中可以工作了。