[轉]OnKickIdle使用

轉自: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處理突然在對話框中可以工作了。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章