<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    weidagang2046的專欄

    物格而后知致
    隨筆 - 8, 文章 - 409, 評論 - 101, 引用 - 0
    數(shù)據(jù)加載中……

    MFC中用戶界面元素更新原理

    ?????大家在編程的過程中一定遇到過這種情況:需要根據(jù)某個變量的值來設定菜單項是否被選中,設置工具欄按鈕是否被按下或者在狀態(tài)欄中顯示一些信息。 MFC 提供了一種機制來幫助我們完成這項工作:只要用 ClassWizard 給相應的菜單項或者工具欄按鈕添加一個 UPDATE_COMMAND_UI 處理函數(shù),在其中用 CcmdUI::SetCheck 等函數(shù)來設置這些用戶界面元素的狀態(tài)就可以了。但是 MFC 是怎么實現(xiàn)這個功能的呢?

    ??? 首先讓我們來看看菜單狀態(tài)更新的實現(xiàn)方法。首先要知道,當你點現(xiàn)了一個有子菜單的菜單項時 ( 比如菜單欄上的“文件” ) ,系統(tǒng)會向擁有這個菜單的窗口發(fā)送一個 WM_INITMENUPOPUP ,下面是 MFC 對這個消息的默認處理: void CFrameWnd::OnInitMenuPopup(CMenu* pMenu, UINT nIndex, BOOL bSysMenu) {

    ??? // 為了說明問題,我省略了很多代碼 ???

    ??? CCmdUI state;

    ???? state.m_pMenu = pMenu;?????? ??????

    ???? state.m_nIndexMax = pMenu->GetMenuItemCount();

    ???? for (state.m_nIndex = 0; state.m_nIndex < state.m_nIndexMax;state.m_nIndex++){

    ?????? ???? state.m_nID = pMenu->GetMenuItemID(state.m_nIndex);

    ?????? ???? if (state.m_nID == (UINT)-1)

    ?????? ???? {

    ????????????? //m_nID==-1 表示它下面還有 popup menu( 就那種帶右箭頭的菜單項 )

    ????????????? // 它是不會自動 deisable ????? ?????????????

    ?????? ???? }

    ?????? ???? else

    ?????? ???? {

    ?????? ????? ??? ?state.m_pSubMenu = NULL;

    ?????? ???? ??? ??state.DoUpdate(this, m_bAutoMenuEnable && state.m_nID < 0xF000);

    ?????? ???? }

    }

    下面是 CCmdUI::DoUpdate 的代碼:

    BOOL CCmdUI::DoUpdate(CCmdTarget* pTarget, BOOL bDisableIfNoHndler){

    ???? m_bEnableChanged = FALSE;

    BOOL bResult=pTarget->OnCmdMsg(m_nID,CN_UPDATE_COMMAND_UI,

    this, NULL);

    ???? if (bDisableIfNoHndler && !m_bEnableChanged){

    ?????? ???? AFX_CMDHANDLERINFO info;

    ?????? ???? info.pTarget = NULL;

    ?????? ???? BOOL bHandler = pTarget->OnCmdMsg(m_nID, CN_COMMAND, this, &info);

    ?????? ???? Enable(bHandler);

    ???? }

    ????? return bResult;

    }

    DoUpdate 的流程就是:先向你的菜單項發(fā)一個 CN_UPDATE_COMMAND_UI 命令消息,讓你的菜單項來進行顯示前的更新,這就是你在classwizard中可以看到的UPDATE_COMMADN_UI消息,你加的處理函數(shù)就是在這個時候被調(diào)用的。如果你處理了CN_UPDATE_COMMAND_UI,那么m_bEnableChanged就變成true,接下來就直接返回了。否則,如果bDisableIfNoHndler也為true,那么就向菜單項發(fā)一個CN_COMMAND消息,如果你不響應這個消息,說明這個菜單項還沒有處理函數(shù),那么,bnHandler就是flase,然后Enable(false)就把你的菜單項變灰了。注意在CFrameWnd::OnInitMenuPopup中調(diào)用DoUpdate時的參數(shù)是m_bAutoMenuEnable && state.m_nID<0xF000,這說如果你一開始就把m_bAutoMenuEnable設為false的話,實際上就關閉了MFC自動diable沒有處理函數(shù)的菜單項的功能。

    ?? 工具欄的更新用的是另外一套方法。首先需要知道當你的的程序變得空閑,沒有消息需要處理的時候, MFC 會調(diào)用 CWinApp::OnIdle 函數(shù)利用這個時間進行一些特殊的工作,其中之一就是更新你的工具欄和狀態(tài)欄。下面來看相關的代碼:

    BOOL CWinThread::OnIdle(LONG lCount){

    ?????? if (lCount <= 0){

    // 依次向 main window 及其所有子窗口發(fā)送 WM_IDLEUPDATECMDUI 消息,這個 消息指示接收窗口進行更新操作

    ????????????? CWnd* pMainWnd = m_pMainWnd;

    ????????????? if (pMainWnd != NULL && pMainWnd->m_hWnd != NULL &&

    ????????????? ?????? pMainWnd->IsWindowVisible())

    ????????????? {

    ????????????? ?????? AfxCallWndProc(pMainWnd, pMainWnd->m_hWnd,

    ???????????????????? ?????? WM_IDLEUPDATECMDUI, (WPARAM)TRUE, 0);

    ????????????? ?????? pMainWnd-> SendMessageToDescendants

    ??????????????????????? ( WM_IDLEUPDATECMDUI, (WPARAM)TRUE, 0, TRUE, TRUE);

    ??????? }

    // 接下來向本線程創(chuàng)建的所有 frame window 發(fā)送 WM_IDLEUPDATECMDUI 消息

    ?????? ?????? AFX_MODULE_THREAD_STATE*? pState=

    _AFX_CMDTARGET_GETSTATE()->m_thread;

    ?????? ?????? CFrameWnd* pFrameWnd = pState->m_frameList;

    ????????????? while (pFrameWnd != NULL){

    ???????????????????? ?if (pFrameWnd->IsWindowVisible()||pFrameWnd->m_nShowDelay >= 0){

    ???????????????????? ?????? ?????? AfxCallWndProc(pFrameWnd, pFrameWnd->m_hWnd,

    ???????????????????? ????????????? ?????? WM_IDLEUPDATECMDUI, (WPARAM)TRUE, 0);

    ???????????????????? ?pFrameWnd->SendMessageToDescendants(WM_IDLEUPDATECMDUI,

    ???????????????????? ????????????? ?????? (WPARAM)TRUE, 0, TRUE, TRUE);

    ???????????????????? ?????? }

    ????????????? }

    ?????? }

    }

    你的 toolbar 或者 statusbar 總是某個 frame window 的子窗口 ( 包括子窗口的子窗口… ) ,所以它肯定能收到 WM_IDLEUPDATECMDUI 消息。 CToolBar CStatusBar 都是從 CControlBar 派生的,下面是 CControlBar 對這個消息的處理:

    LRESULT CControlBar::OnIdleUpdateCmdUI(WPARAM wParam, LPARAM)

    {

    ?????? if ((GetStyle() & WS_VISIBLE) )

    ?????? {

    ?????? ?? // pTarget 指向離 this 最近的父 frame window

    ?????? ?????? CFrameWnd* pTarget = (CFrameWnd*)GetOwner();

    ????????????? if (pTarget == NULL || !pTarget->IsFrameWnd())

    ????????????? ?????? pTarget = GetParentFrame();

    ????????????? // 調(diào)用虛成員函數(shù) OnUpdateCmdUI

    ????????????? if (pTarget != NULL)

    ????????????? ?????? OnUpdateCmdUI(pTarget, (BOOL)wParam);

    ?????? }

    ?????? return 0L;

    }

    OnUpdateCmdUI CControlBar 類的一個純虛函數(shù), CToolBar 中對這個函數(shù)進行了定義:

    void CToolBar::OnUpdateCmdUI(CFrameWnd* pTarget, BOOL bDisableIfNoHndler){

    ?????? CToolCmdUI state;?

    ?????? state.m_pOther = this;

    ?????? state.m_nIndexMax = DefWindowProc(TB_BUTTONCOUNT, 0, 0); // 工具欄上的按鈕數(shù)

    ?????? for (state.m_nIndex=0; state.m_nIndex < state.m_nIndexMax; state.m_nIndex++){

    ?????? ? // 如果你派生了自己的 CToolBar 類,那么先讓執(zhí)行你定義的處理函數(shù)來進行狀態(tài)更新

    ?????? ? if (CWnd::OnCmdMsg(state.m_nID, CN_UPDATE_COMMAND_UI, &state, NULL))

    ???????????????????? ?????? continue;

    // 如果 toolbar 沒有更新自己,讓 pTarget( 也就是離它最近的父 frame window) 來更新它。 比如對于 MFC 自動生成的 SDI 框架來說, pTarget 會指向 CMainFrame

    ????????????? ?????? state.DoUpdate(pTarget, bDisableIfNoHndler);

    ????????????? }

    ?????? }

    ?????? // 如果 CToolBar 中有用戶創(chuàng)建的控件,也一起更新

    ?????? UpdateDialogControls(pTarget, bDisableIfNoHndler);

    }

    CCmdUI::DoUpdate 的代碼上面已經(jīng)列出過了。至此,工具欄和狀態(tài)欄也能順利也進行更了。

    有經(jīng)驗的朋友應該知道,如果你在一個基于對話框的程序里模仿 doc/view 結構中的方法使用 UPDATE_COMMAND_UI 來更新用戶界面元素的話是不會有任何效果的。其原因是一個模態(tài)對話顯示出來以后,程序就會進入這個對話框自己的消息循環(huán) ( 看看 DoModal 的源碼就能了解這一點 ) ,此時不會再有 WM_IDLEUPDATECMDUI 被發(fā)送到這些界面元素中。下面說說這種情況下的解決辦法,你可以自己查看 MFC 的源碼來弄清它的原理:首先加一個頭文件 afxpriv.h(其中定義了KICKIDLE消息) ,然后添加一個消息映射來處理 WM_KICKIDLE消息:ON_MESSAGE(WM_KICKIDLE,OnKickIdle)。其中OnKickIdle定義如下:

    LRESULT CTabDialog::OnKickIdle(WPARAM wp, LPARAM lCount){

    ?UpdateDialogControls(this, TRUE);

    return 0;

    }

    完成這些工作以后 , 你就可以順利地使用 UPDATE_COMMAND_UI 機制了。

    from: http://www.zahui.com/html/1/2881.htm

    posted on 2006-08-29 16:57 weidagang2046 閱讀(1194) 評論(0)  編輯  收藏 所屬分類: Windows

    主站蜘蛛池模板: 一个人看的www在线观看免费| 久久99亚洲网美利坚合众国 | 精品国产亚洲一区二区在线观看| 亚洲中文字幕久久精品无码A| 亚洲黄色免费电影| 久久亚洲国产视频| 国产一精品一av一免费爽爽| 国产亚洲情侣一区二区无码AV | 国产做床爱无遮挡免费视频| 亚洲中文字幕精品久久| 中文字幕影片免费在线观看 | 国产91久久久久久久免费| 亚洲国产午夜精品理论片在线播放| 69成人免费视频| 亚洲一级毛片在线播放| 曰批全过程免费视频网址| 67pao强力打造67194在线午夜亚洲| 午夜免费福利小电影| 无码乱人伦一区二区亚洲一| 免费观看久久精彩视频| 亚洲av无码成h人动漫无遮挡| 国内精品免费视频精选在线观看| 亚洲国产精品成人精品无码区 | 免费电视剧在线观看| 四虎亚洲精品高清在线观看| 猫咪社区免费资源在线观看| 亚洲娇小性xxxx| 99久久免费国产精品特黄| 亚洲中文字幕一二三四区苍井空 | 特级无码毛片免费视频 | 亚洲色av性色在线观无码| 最近的中文字幕大全免费8| 亚洲精品成人图区| 男女免费观看在线爽爽爽视频 | ww在线观视频免费观看| 亚洲成在人线中文字幕| 无人在线观看免费高清视频| 亚洲女女女同性video| 国产免费爽爽视频免费可以看| 美女裸免费观看网站| 不卡精品国产_亚洲人成在线|