Make a menu

Hi,

On my main menu I have a drop down titled 'stuff', which has like 5 sub menu items:

    Stuff
      |- Apple
      |- Orange
      |- Pear

I'd like to have a sub menu for 'Orange', so when the user mouses over, a menu comes out of it with additional items. This is easy to do with the vc++ editor. But I need to read the number of items to put in there from disk, something like:

void CMainFrame::GetDropdownMenuItems(vector<CString> &vItems)
{
     // Populate vItems like 'itemA', 'itemB', 'itemC'
}

Then the menu should look like:

    Stuff
      |- Apple
      |- Orange ---- itemA
      |- Pear           itemB
                           itemC

I'm just not sure where to override what in order to get a chance to dynamically build the sub menu for 'Orange'?

Thanks
LVL 7
minnirokAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

DanRollinsCommented:
In the menu editor create a "stub" submenu with just one dummy item.

At runtime, call
    CMenu::GetMenuItemInfo()
for the "parent" of the cascading menu.  The value of
    MENUITEMINFO.hSubMenu
is an HMENU that you can attach to a new CMenu and you can now add and replace items at will.

I'll provide code and details if that is not enough.

-- Dan
0
AndyAinscowFreelance programmer / ConsultantCommented:
You could trap the WM_INITMENU message (in the MainFrame for example) and at that point modify the menu.  
0
AndyAinscowFreelance programmer / ConsultantCommented:
There is also a WM_INITMENUPOPUP - this should be fired when a popup menu is to be shown.  This might be more suitable to trap and modify the sub menu by adding the itemA, B and C that you want.
0
Cloud Class® Course: Certified Penetration Testing

This CPTE Certified Penetration Testing Engineer course covers everything you need to know about becoming a Certified Penetration Testing Engineer. Career Path: Professional roles include Ethical Hackers, Security Consultants, System Administrators, and Chief Security Officers.

minnirokAuthor Commented:
Hi,

@ Dan
Could you provide the code please?

@ Andy
When WM_INITMENU is called in mainframe, how will I know it is in fact this particular menu that is being popped up at that moment, compared to any of the other menus I have in mainframe?

Thanks
0
AndyAinscowFreelance programmer / ConsultantCommented:
<When WM_INITMENU is called in mainframe, how will I know it is in fact this particular menu that is being popped up at that moment, compared to any of the other menus I have in mainframe?>

I've not tested this but it should work (maybe there is a simpler way).  You get the ID of the first item.  If it is ID_MENU_APPLE then it is the menu you want to modify.
0
minnirokAuthor Commented:
Ahh hmm not sure if I did this right - I added a handler for WM_INITMENU to my child frame class (this menu item is for the child frame only) but no WM_INITMENU is ever getting sent, this never executes:

void CChildFrame::OnInitMenu(CMenu* pMenu)
{
    CMDIChildWnd::OnInitMenu(pMenu);

    AfxMessageBox("yes this handler was called");

    CString s;
    s.Format("the id is %i compared to %i", pMenu->GetMenuItemID(0),  ID_MENU_ITEM_APPLE);
    AfxMessageBox(s);
}
0
DanRollinsCommented:
#define MID_OrangeSub1 2001
#define MID_OrangeSub2 2002
#define MID_OrangeSub3 2003

...
BEGIN_MESSAGE_MAP(CMainFrame, CMDIFrameWnd)
      //{{AFX_MSG_MAP(CMainFrame)
...
      ON_WM_INITMENUPOPUP()
      ON_UPDATE_COMMAND_UI_RANGE( MID_OrangeSub1, MID_OrangeSub3, OnUpdateOrangeSubs )   // added manually
      //}}AFX_MSG_MAP
END_MESSAGE_MAP()

void CMainFrame::OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu)
{
      BOOL fRet;

      CMDIFrameWnd::OnInitMenuPopup(pPopupMenu, nIndex, bSysMenu);
      
      CString sMenuText;
      pPopupMenu->GetMenuString( 2, sMenuText, MF_BYPOSITION ); // eyeball check for debugging
      CMenu* pmnuOrange= pPopupMenu->GetSubMenu(2);
      if ( pmnuOrange == NULL ) {
            return;
      }
      pmnuOrange->GetMenuString( 0, sMenuText, MF_BYPOSITION );  // debugging eyeball verify

      pmnuOrange->RemoveMenu( 0, MF_BYPOSITION );  // remove the stub

      pmnuOrange->AppendMenu(MF_STRING, MID_OrangeSub1, "OrangeSub&1" );
      pmnuOrange->AppendMenu(MF_STRING, MID_OrangeSub2, "OrangeSub&2" );
      pmnuOrange->AppendMenu(MF_STRING, MID_OrangeSub2, "OrangeSub&3" );
}

void CMainFrame::OnUpdateOrangeSubs(CCmdUI* pCmdUI)
{
      pCmdUI->Enable(TRUE);
}
0
DanRollinsCommented:
In the resource editor, I added a menu item labled "Orange" to an existing menu in an MDI app.   I set its properties to put a checkmark in the "Pop-Up" checkbox.
I then added an item to the resulting submenu and labeled it "item_stub" with an arbitrary command IS.

The rest  (all the stuff about OnUpdate and ON_UPDATE_COMMAND_UI_RANGE) is related to having the system enable the items so they won't be gray in the menu (unless there is a ON_COMMAND handler, they are gray and disabled)

-- Dan
0
DanRollinsCommented:
One other thing I just noticed... if the menus have already been added, you need to avoid adding them again :-) The simplest check might be to see if there are already three items.  For instance...

      if ( pmnuOrange->GetMenuItemCount() > 1 ) {
            ... do nothing...
      } else {
            ... delete item 0, and append the others...
      }
0
AndyAinscowFreelance programmer / ConsultantCommented:
Next day - an idea.
      CMenu* pMenu = GetMenu();
      CMenu* pSubMenu = pMenu->GetSubMenu(0);
      pSubMenu->InsertMenu(0, MF_BYPOSITION, 12345, _T("Hello"));

You can use code like the above to modify menus when you want to make a change.  For example in an SDI app you could use the code in the OnCreate of the main frame.  An MDI is slightly more complex (it has multiple menus depending on no view or one/many views).
Here you would change the menu once - rather than intercepting a message indicating a menu is to be shown and modifying/checking on multiple occasions.

This is probably better than modifying on the fly unless you have a dynamic menu (eg. undo showing the last 6 actions)
0
minnirokAuthor Commented:
Hi guys,

Ok so Dan I  tried your method, but I don't understand what this is doing:

     CString sMenuText;
     pPopupMenu->GetMenuString( 2, sMenuText, MF_BYPOSITION ); // eyeball check for debugging
     CMenu* pmnuOrange= pPopupMenu->GetSubMenu(2);
     if ( pmnuOrange == NULL ) {
          return;
     }

Why are you using '2' for GetMenuString()? Also, sMenuText always comes out emtpy after that call, pmnuOrange keeps coming out NULL too - ok so I'm doing something wrong, I just want to know what you're trying to do there.

Andy, everytime the user is going to mouse over this menu item, I have to scan a directory for XML files and display their names on the menu, so it has to be generated everytime they popup this menu. It is really unfortunate!

Thanks guys
0
minnirokAuthor Commented:
If I could just find the ID of the pPopupMenu I could compare it to the id of the menu resource I created in the editor, and if the index is the same I know they're about to popup the dynamic item.

I cannot find a way to get the resource ID of the passed pPopupMenu though, good gravy,

Thanks

void CMainFrame::OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu)
{
    if (pPopupMenu.m_resID == MENU_ORANGE && nIndex == nIndexOfDynamicSubPopup) {

        // Now build the dynamic sub menu popup?
    }
}
0
AndyAinscowFreelance programmer / ConsultantCommented:
Question - do you want the menu creating once (when the app runs) or can it vary during the lifetime of the app?
If just once see my previous comment.
0
minnirokAuthor Commented:
Hi Andy,

Yeah it will vary during the lifetime - say they dump 3 XML's in my directory, when they popup the menu it should show those 3 XML filenames - if they add 10 more XML's to the dir and popup the menu again, it should show 10 filenames now,

I still can't find a way to figure which menu it is that's being worked with in OnInitMenuPopup, it is most cruel!

Thanks
0
AndyAinscowFreelance programmer / ConsultantCommented:
Next question.
Is it the main menu bar at the top we are talking about or a context menu (mouse right button click)?
0
minnirokAuthor Commented:
It's the main menu - more specifically, its a child frame menu, so when you open up one of my documents that menu becomes the main menu bar,

Thanks
0
AndyAinscowFreelance programmer / ConsultantCommented:
OK try this code
void CMainFrame::OnInitMenu(CMenu* pMenu)
{
      CMDIFrameWnd::OnInitMenu(pMenu);

      // TODO: Add your message handler code here
      CString s0, s1, s2;
      pMenu->GetMenuString(0, s0, MF_BYPOSITION);
      pMenu->GetMenuString(1, s1, MF_BYPOSITION);
      pMenu->GetMenuString(2, s2, MF_BYPOSITION);
      TRACE(_T("Menu Item 0-%s, 1-%s, 2-%s\n"), s0, s1, s2);
}


you should see it giving the 1-&File, 1-&Edit, 2-&View

So you see that pMenu is pointing to the menu bar and you can get the items on it.
0
AndyAinscowFreelance programmer / ConsultantCommented:
Now try this piece of code

void CMainFrame::OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu)
{
      CMDIFrameWnd::OnInitMenuPopup(pPopupMenu, nIndex, bSysMenu);

      // TODO: Add your message handler code here
      CString s0, s1, s2;
      pPopupMenu->GetMenuString(0, s0, MF_BYPOSITION);
      pPopupMenu->GetMenuString(1, s1, MF_BYPOSITION);
      pPopupMenu->GetMenuString(2, s2, MF_BYPOSITION);
      TRACE(_T("Menu Item (index %d) 0-%s, 1-%s, 2-%s\n"), nIndex, s0, s1, s2);
}

Now you should see it fire as you change the selection in the menu bar.
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
AndyAinscowFreelance programmer / ConsultantCommented:
You can use the previous two code snippets to check if it is your popup menu being opened (check text and index).

When the popup menu is being opened (pPopupMenu) then you can add/insert items to it on the fly
eg.
pPopupMenu->InsertMenu(0, MF_BYPOSITION, 12345, _T("Hello"));
0
minnirokAuthor Commented:
Hi Andy,

That's great, finally some progress! I'd like to open another question on this because I need some help actually creating that new menu, so if you can check the question listings in a minute, that would be great.

Thanks

0
DanRollinsCommented:
>>Why are you using '2' for GetMenuString()?
The '2' is the position on the pre-built menu of the item that is a submenu.  The "eyeball check" is to help you ensure that you have found that menu during your debugging (for instance, it might be item #3).  If you were to find that it was item number 3 then your next step would be to modify the code I provided and in the places where I said 2, you would use 3 instead.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
System Programming

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.