ダイアログのメニューでON_UPDATE_COMMAND_UIを使う

ダイアログに追加したメニューに対しては通常では、ON_UPDATE_COMMAND_UIメッセージが飛ばない。

ドロップダウンメニューが表示される際に、WM_INITMENUPOPUPメッセージがメニュー項目を表示する前に送信される。
CFrameWnd::OnInitMenuPopup 関数は、メニュー項目を反復し、アイテムに対し更新コマンド UI ハンドラーを呼び出す。
CDialogクラスでは、この動作がサポートされていないのでメニューを表示してもON_UPDATE_COMMAND_UIが飛ばない。
WM_INITMENUPOPUP のハンドラをオーバーライドして、メニューアイテムに更新コマンド UI ハンドラーを呼び出すようにする。

Microsoftでこの方法が記載されている。
http://support.microsoft.com/kb/242577/ja


1. メッセージ マップに ON_WM_INITMENUPOPUP エントリを追加:
BEGIN_MESSAGE_MAP(CTestDlg, CDialog)
  //}}AFX_MSG_MAP
  ON_WM_INITMENUPOPUP()
END_MESSAGE_MAP()


2. ダイアログ ボックス クラスに OnInitMenuPopup メンバー関数を追加し、次のコードを記述
  (WinFrm.cpp の CFrameWnd::OnInitMenuPopup を一部直したもの)
void CTestDlg::OnInitMenuPopup(CMenu *pPopupMenu, UINT nIndex,BOOL bSysMenu)
{
  ASSERT(pPopupMenu != NULL);
  // Check the enabled state of various menu items.

  CCmdUI state;
  state.m_pMenu = pPopupMenu;
  ASSERT(state.m_pOther == NULL);
  ASSERT(state.m_pParentMenu == NULL);

  // Determine if menu is popup in top-level menu and set m_pOther to
  // it if so (m_pParentMenu == NULL indicates that it is secondary popup).
  HMENU hParentMenu;
  if (AfxGetThreadState()->m_hTrackingMenu == pPopupMenu->m_hMenu)
    state.m_pParentMenu = pPopupMenu; // Parent == child for tracking popup.
  else if ( (hParentMenu = ::GetMenu(m_hWnd)) != NULL)
  {
    CWnd* pParent = this;
    // Child windows don't have menus--need to go to the top!
    if (pParent != NULL &&
      (hParentMenu = ::GetMenu(pParent->m_hWnd)) != NULL)
    {
      int nIndexMax = ::GetMenuItemCount(hParentMenu);
      for (int nIndex = 0; nIndex < nIndexMax; nIndex++)
      {
        if (::GetSubMenu(hParentMenu, nIndex) == pPopupMenu->m_hMenu)
        {
          // When popup is found, m_pParentMenu is containing menu.
          state.m_pParentMenu = CMenu::FromHandle(hParentMenu);
          break;
        }
      }
    }
  }

  state.m_nIndexMax = pPopupMenu->GetMenuItemCount();
  for (state.m_nIndex = 0; state.m_nIndex < state.m_nIndexMax;
    state.m_nIndex++)
  {
    state.m_nID = pPopupMenu->GetMenuItemID(state.m_nIndex);
    if (state.m_nID == 0)
      continue; // Menu separator or invalid cmd - ignore it.

    ASSERT(state.m_pOther == NULL);
    ASSERT(state.m_pMenu != NULL);
    if (state.m_nID == (UINT)-1)
    {
      // Possibly a popup menu, route to first item of that popup.
      state.m_pSubMenu = pPopupMenu->GetSubMenu(state.m_nIndex);
      if (state.m_pSubMenu == NULL ||
        (state.m_nID = state.m_pSubMenu->GetMenuItemID(0)) == 0 ||
        state.m_nID == (UINT)-1)
      {
        continue; // First item of popup can't be routed to.
      }
      state.DoUpdate(this, TRUE); // Popups are never auto disabled.
    }
    else
    {
      // Normal menu item.
      // Auto enable/disable if frame window has m_bAutoMenuEnable
      // set and command is _not_ a system command.
      state.m_pSubMenu = NULL;
      state.DoUpdate(this, FALSE);
    }

    // Adjust for menu deletions and additions.
    UINT nCount = pPopupMenu->GetMenuItemCount();
    if (nCount < state.m_nIndexMax)
    {
      state.m_nIndex -= (state.m_nIndexMax - nCount);
      while (state.m_nIndex < nCount &&
        pPopupMenu->GetMenuItemID(state.m_nIndex) == state.m_nID)
      {
        state.m_nIndex++;
      }
    }
    state.m_nIndexMax = nCount;
  }
}



最終更新:2011年09月27日 17:14