Using ON_UPDATE_COMMAND_UI with menu items and controls (2)

-->

 If you have a dialog-based application, to which you have added a menu, any OnUpdate handlers that you included in the dialog's message map will never be called.  (Note that the corresponding OnCommand handlers work fine).

By handling the dialog's WM_KICKIDLE message, and mimicking MFC's routing of its UPDATE_UI messages, you can implement this with a little work.

The first step is to handle WM_KICKIDLE, which is #defined in afxpriv.h, by declaring OnKickIdle(), and adding it to the message map.

//[in TESTDLG.H]
class CTestDlg : public CDialog
{
 . . .
 // Generated message map functions
 //{{AFX_MSG(CTestDlg)
 . . .
 //}}AFX_MSG
 afx_msg LRESULT OnKickIdle(WPARAM, LPARAM);
 DECLARE_MESSAGE_MAP()
 . . .
};
#include "stdafx.h"
#include "TestDlg.h"
#include <afxpriv.h>
. . . 
BEGIN_MESSAGE_MAP(CTestDlg, CDialog)
 //{{AFX_MSG_MAP(CTestDlg)
 . . .
 //}}AFX_MSG_MAP
 ON_MESSAGE(WM_KICKIDLE, OnKickIdle)
END_MESSAGE_MAP()

The real work comes in defining OnKickIdle(). You need to iterate over each item that might have an OnUpdate handler, set up a CCmdUI instance for that item, and call CCmdUI::DoUpdate().  The OnKickIdle() I have written will update the menu items in each of the first-level dropdown menus (e.g. the "File" menu, the "Edit" menu, etc. from a normal app).  You will need to modify it if you have further levels of submenus.  You will need a similar loop if you want to have OnUpdate handlers for a status bar, toolbar, or specific dialog controls.

LRESULT CTestDlg::OnKickIdle(WPARAM, LPARAM)
{
 CMenu* pMainMenu = GetMenu();
 CCmdUI cmdUI;
 for (UINT n = 0; n < pMainMenu->GetMenuItemCount(); ++n)
 {
  CMenu* pSubMenu = pMainMenu->GetSubMenu(n);
  cmdUI.m_nIndexMax = pSubMenu->GetMenuItemCount();
  for (UINT i = 0; i < cmdUI.m_nIndexMax;++i)
  {
   cmdUI.m_nIndex = i;
   cmdUI.m_nID = pSubMenu->GetMenuItemID(i);
   cmdUI.m_pMenu = pSubMenu;
   cmdUI.DoUpdate(this, FALSE);
  }
 }
 return TRUE;
}

Download demo project - 10 KB



Comments

  • Handling OnUpdate() processing for menu items

    Posted by Legacy on 10/16/2001 12:00am

    Originally posted by: Anonymous

    How to do this for a CPropertyPage derived class ?????

    Please help...

    Reply
  • Smaller, faster and efficient (I hope) K.I.S.S. in other word.

    Posted by Legacy on 06/14/2001 12:00am

    Originally posted by: Leonid Prokopovich

    There is no need to update menu items in all submenus. You should update only those one which about to show.
    
    There is simple and efficient solution. I'm using it for a long time.

    1. Add WM_INITMENUPOPUP handler.
    2. Modify OnInitMenuPopup() as follows

    void CMyDlg::OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu)
    {
    CCmdUI CmdUI;
    UINT nIndex;

    // Skip system menu from processing
    if( FALSE != bSysMenu )
    return;

    CmdUI.m_nIndexMax = pPopupMenu->GetMenuItemCount();
    for( nIndex = 0; nIndex < CmdUI.m_nIndexMax; ++nIndex )
    {
    CmdUI.m_nIndex = nIndex;
    CmdUI.m_nID = pPopupMenu->GetMenuItemID(i);
    CmdUI.m_pMenu = pPopupMenu;

    // There are two options. You can choose the one by removing comments
    // Option 1. All handlers are in dialog
    // CmdUI.DoUpdate( this, TRUE );

    // Option 2. There are handlers in dialog and controls
    /*
    CmdUI.DoUpdate( this, FALSE );
    // If dialog handler doesn't change state route update
    // request to child controls. The last DoUpdate will
    // disable menu item with no handler
    if( FALSE == CmdUI.m_bEnableChanged )
    CmdUI.DoUpdate( m_pControl_1, FALSE );
    ...
    if( FALSE == CmdUI.m_bEnableChanged )
    CmdUI.DoUpdate( m_pControl_Last, TRUE );
    */
    }
    }

    Reply
  • Avoid KickIdle,A simple way toUpdate!!

    Posted by Legacy on 11/30/2000 12:00am

    Originally posted by: Sreedharan

    Hi,
    
    I am using OnMenuSelect() function rather.
    So, cmdUI.DoUpdate() function is called whenever user
    Selects a menu item,instead of calling it from OnKickIdle().

    void CXXXDlg::OnMenuSelect(UINT nItemID, UINT nFlags, HMENU hSysMenu)
    {
    CCmdUI cmdUI;
    CMenu* pMainMenu = GetMenu();
    CMenu* pSubMenu = pMainMenu-> GetSubMenu(nItemID);
    if(IsMenu(pSubMenu->GetSafeHmenu()))
    {
    cmdUI.m_nIndexMax = pSubMenu->GetMenuItemCount();
    for (UINT i = 0; i < cmdUI.m_nIndexMax;++i)
    {
    cmdUI.m_nIndex = i;
    cmdUI.m_nID = pSubMenu->GetMenuItemID(i);
    cmdUI.m_pMenu = pSubMenu;
    cmdUI.DoUpdate(this, FALSE);
    }
    }

    }


    Regards,
    Sreedharan

    Reply
  • Re : Code hoards CPU time

    Posted by Legacy on 11/02/2000 12:00am

    Originally posted by: Yair Konfino

    It`s true that the CPU is working hard in this case !!!
    Spy++ shows why .
    WM_KICKIDLE is passed to the window thousands of times
    repeatedly .
    The solution is to use :
    void OnKickIdle() and ON_MESSAGE_VOID(ID_XXXX,WM_KICKIDLE).
    It`s working for me .

    Reply
  • Updating popup menus

    Posted by Legacy on 07/25/2000 12:00am

    Originally posted by: Damian Biollo

    John Wismar's code works good, except that OnKickIdle() doesn't get called during TrackPopupMenu().  
    
    

    A simpler solution is to handle the WM_INITMENUPOPUP message; OnInitMenuPopup() will be called automatically for each popup sub-menu, so you don't even need to code the UpdateMenu function recursively.

    As a bonus, MS has actually documented OnInitMenuPopup(), unlike the undocumented kick-idle.

    Like so:

    BEGIN_MESSAGE_MAP(...)
    ON_WM_INITMENUPOPUP()
    END_MESSAGE_MAP()

    void MyDialog::OnInitMenuPopup(CMenu* pMenu, UINT nIndex, BOOL bSysMenu)
    {
    UpdateMenu(pMenu);
    }

    void MyDialog::UpdateMenu(CMenu* pMenu)
    {
    CCmdUI cmdUI;
    cmdUI.m_nIndexMax = pMenu->GetMenuItemCount();
    for (UINT n = 0; n < cmdUI.m_nIndexMax; ++n)
    {
    CMenu* pSubMenu = pMenu->GetSubMenu(n);
    if (pSubMenu == NULL)
    {
    cmdUI.m_nIndex = n;
    cmdUI.m_nID = pMenu->GetMenuItemID(n);
    cmdUI.m_pMenu = pMenu;
    cmdUI.DoUpdate(this, FALSE);
    }
    }
    }


    Reply
  • Code hoards CPU time

    Posted by Legacy on 03/02/2000 12:00am

    Originally posted by: Clinton Morell

    I found that the OnKickIdle() code tended to hoard CPU time on my computer and it was keeping other programs from getting enough CPU cycles.

    Adding "Sleep(1)" at the beginning of the OnKickIdle() function fixed the problem for me, your mileage may vary.

    Reply
  • How to implement this for a toolbar control.

    Posted by Legacy on 04/19/1999 12:00am

    Originally posted by: Brandon Jackson

    First, follow Mr. Wismar's instructions for adding OnUpdate() handling for menus.
    
    

    Then, from ../MFC/SRC/BARTOOL.CPP, grab the definition for CToolCmdUI.

    In OnKickIdle() add

    (m_tbr is a CToolBar derived class)


    CToolCmdUI state;
    state.m_pOther = &m_tbr;

    state.m_nIndexMax = m_tbr.GetToolBarCtrl().GetButtonCount();
    for ( state.m_nIndex = 0; state.m_nIndex < state.m_nIndexMax; state.m_nIndex++ )
    {
    state.m_nID = m_tbr.GetItemID( state.m_nIndex );

    // ignore separators
    if (!(m_tbr.GetButtonStyle( state.m_nIndex ) & TBSTYLE_SEP))
    {
    // allow the owner to process the update
    state.DoUpdate( this, TRUE );
    }
    }

    Reply
  • How to implement this for toolbar control?

    Posted by Legacy on 04/15/1999 12:00am

    Originally posted by: Andrey

    I have a Tabbed Property sheet application.

    The first Property page has a toolbar control and a list control. The list control has a popup menu with commands applied to it. It has ON_UPDATE_COMMAND_UI for these commands as it knows when to disable items.

    I need that toolbar control that belongs to list's parent to duplicate the commands in the menu (including auto disabling).

    Do you know how to implement this?

    Thanks a lot!

    Reply
  • You must have javascript enabled in order to post comments.

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • When it comes to desktops – physical or virtual – it's all about the applications. Cloud-hosted virtual desktops are growing fast because you get local data center-class security and 24x7 access with the complete personalization and flexibility of your own desktop. Organizations make five common mistakes when it comes to planning and implementing their application management strategy. This eBook tells you what they are and how to avoid them, and offers real-life case studies on customers who didn't let …

  • The latest release of SugarCRM's flagship product gives users new tools to build extraordinary customer relationships. Read an in-depth analysis of SugarCRM's enhanced ability to help companies execute their customer-facing initiatives from Ovum, a leading technology research firm.

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds