C++ CMenu Drawing Incorrectly (Non-custom) - c++

I currently am attempting to do something simple:
CMenu menu;
menu.LoadMenu(IDR_MENU_IMAGE);
CPoint pt;
GetCursorPos(&pt);
menu.TrackPopupMenu(TPM_RIGHTBUTTON, pt.x, pt.y, this);
When I right-click, I get the below image. However, the menu is loaded; as I move my cursor down the menu, you can see it populate sub-menus just fine. It does this with any menu I load dynamically like this. I attempted to create a pointer (CMenu*) and still continue having this problem.
The points don't matter (I input arbitrary points).
The "this" in question is a derived CView* class. I am clicking on a HWND object but I tried to also take the CWnd::FromHandle() of this particular object I am clicking on and having the object handle it, but I still have the same problem.
My top menu structure and all other menus work - it is only in this particular case.
I don't really want to derive a C++ CMenu class just to override the MeasureItem function when the original menus should actually be working, and do work fine in other versions..
Help?

Use GetSubMenu(0) to obtain a popup handle:
CMenu menu;
menu.LoadMenu(IDR_MENU_IMAGE);
CMenu *submenu = menu.GetSubMenu(0);
if (submenu)
submenu->TrackPopupMenu(TPM_RIGHTBUTTON, pt.x, pt.y, this);
Where IDR_MENU_IMAGE is created in resource editor similar to the following:
IDR_MENU_IMAGE MENU
BEGIN
POPUP "File"
BEGIN
MENUITEM "New", ID_FILE_NEW
MENUITEM "Open", ID_FILE_OPEN
MENUITEM "Save", ID_FILE_SAVE
MENUITEM "Save As ...", ID_FILE_SAVEAS
END
END
Result:
Note, this won't work if there is only a "menu bar", and no popup. The menu below cannot be created as popup:
IDR_MENU_IMAGE MENU //no popup menu!
BEGIN
MENUITEM "A", IDA
MENUITEM "B", IDB
MENUITEM "C", IDC
END
You can also create the popup menu as follows:
CMenu menu;
menu.CreatePopupMenu();
menu.AppendMenu(MF_STRING, ID_FILE_NEW, "New");
menu.TrackPopupMenu(TPM_RIGHTBUTTON, p.x, p.y, this);

Related

Can I disable a menu item on the menu bar and / or dynamically display it?

Here is a new View menu that I have added to my software:
My editor (a CDialog) has two modes. This View menu is only application to one of the modes.
At the moment I am simply disabling the menu items like this:
CMenu* pMenu = GetMenu();
if (pMenu != nullptr)
{
pMenu->EnableMenuItem(ID_VIEW_REFRESH, MF_BYCOMMAND | MF_GRAYED);
CMenu* pViewMenu = pMenu->GetSubMenu(3);
if (pViewMenu != nullptr)
pViewMenu->EnableMenuItem(1, MF_BYPOSITION | MF_GRAYED);
}
This works fine. But is there any way to either:
Disable the actual View menu item on the menu bar?
Remove / Add the menu as needed?
As the moment the menu is always there and I just disable the items dependant on the active editor mode. It is part of my editor menu in the resources:
POPUP "View"
BEGIN
MENUITEM "Refresh\tF5", ID_VIEW_REFRESH, INACTIVE
POPUP "Zoom", GRAYED
BEGIN
MENUITEM "Zoom In\tCTRL +", ID_ZOOMLEVEL_ZOOMIN
MENUITEM "Zoom Out\tCTRL -", ID_ZOOMLEVEL_ZOOMOUT
MENUITEM SEPARATOR
MENUITEM "400%", ID_ZOOMLEVEL_400
MENUITEM "300%", ID_ZOOMLEVEL_300
MENUITEM "250%", ID_ZOOMLEVEL_250
MENUITEM "200%", ID_ZOOMLEVEL_200
MENUITEM "175%", ID_ZOOMLEVEL_175
MENUITEM "150%", ID_ZOOMLEVEL_150
MENUITEM "125%", ID_ZOOMLEVEL_125
MENUITEM "100%\tCTRL + 0", ID_ZOOMLEVEL_100
MENUITEM "75%", ID_ZOOMLEVEL_75
MENUITEM "50%", ID_ZOOMLEVEL_50
MENUITEM SEPARATOR
MENUITEM "Custom...", ID_ZOOM_CUSTOM
END
END
Is this possible?
Let's suppose your menu is called IDR_MAINFRAME:
Create your mainFrame and add IDR_MAINFRAME menu:
CMainFrame* pFrame = new CMainFrame;
pFrame->LoadFrame(IDR_MAINFRAME, WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE, NULL, NULL);
You must get the main menu like this:
CMenu menu;
menu.LoadMenu(IDR_MAINFRAME);
Now, you can desable a specific item:
menu.EnableMenuItem (1, MF_BYPOSITION|MF_DISABLED|MF_GRAYED);
pFrame->SetMenu(&menu);
See result below :
Note that Edition is a main menu (similar to your View menu) for my application.
To enable your menu (View menu) dynamically, call EnableMenuItem a second time like this:
menu.EnableMenuItem (1, MF_BYPOSITION);
Hope it help you.
Update
I also had to use this code to get the menu bar to update visually:
DrawMenuBar();
With this code the menu did not visually update until the mouse was put over the menu text.
Sure, to disable the item retrieve the menu handle using GetMenu then use the EnableMenuItem API and specify MF_BYPOSITION rather than MF_BYCOMMAND.
Or you could use a MENUEX resource and assign an ID to the popup menu items (unfortunately th resource editor can not save MENUEX resources, it can read them but always saves as MENU). If you want to change to MENUEX put it in the .rc2 file of an MFC project.

How to draw a custom menu on MFC SDI dynamically

I am trying to draw a custom popup menu on MFC single-document interface (SDI) project.
From here, I found The framework calls this member function for the owner of an owner-draw button control, combo-box control, list-box control, or menu when a visual aspect of the control or menu has changed.
So I added the handle OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct) and added some code inside this function, but I found that this callback didn't be called by the framework when I right click on the view, like this:
How to make a popup menu dynamically?
Here is how I get my pop up menu:
void Cdynamic_menu_sdiView::OnContextMenu(CWnd* /* pWnd */, CPoint point)
{
if (IDR_MY_MENU == 0)
return;
CMenu dynamicMenu, proxyMenu;
if (dynamicMenu.GetSafeHmenu())
dynamicMenu.DestroyMenu();
// Create a new popup menu.
if (dynamicMenu.CreatePopupMenu() == FALSE)
return;
if (proxyMenu.LoadMenu(IDR_MY_MENU) == FALSE)
return;
int nSubMenu = 1;
CMenu* pProxyMenu = proxyMenu.GetSubMenu(nSubMenu);
build_dynamic_menu(m_map_menu_element_2, pProxyMenu, dynamicMenu, CString(""));
//m_menu_on_draw = &dynamicMenu; // link up the dynamic menu to the on draw menu, so that we can print it on the screen
CPoint ptCurPos; // current cursor position
GetCursorPos(&ptCurPos);
dynamicMenu.TrackPopupMenu(TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON, ptCurPos.x, ptCurPos.y, AfxGetMainWnd(), NULL);
}
Inside the function build_dynamic_menu(), it is a depth first search function, I do InsertMenu(i, MF_BYPOSITION | MF_POPUP | MF_OWNERDRAW, uSubMenuID, strLabel); so that I can change the text of the popup menu dynamically.
Since it is too long to put the function here, I only state the idea.
I also want to change the text color and background color dynamically, so I am trying to find a way to draw the menu by code.
Where should I start? Is owner drawn menus approach good for me?

Get controller who has focus inside CWnd

I've a dialog, CFormView, which holds some buttons and a panel which holds Tabcontrol, radiobuttons, text input fields etc.
So, on my panel, the CWnd, I create my input fields like this:
pEdit = new CEdit();
pEdit->CreateEx(WS_EX_CLIENTEDGE, _T("EDIT"), NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | nAttrMultiline | m_clRect, pclPanel, iID)
Where m_clRect is a CRect, pclPanel is my CWnd, and iID is just the controller ID.
I want to fill my CEdit with text when a button is clicked, but somehow I can't get the controller who has focus.
My first attempt was to call GetFocus(), cast it into a CEdit and add the text, but this just changes the text on my button, of course.
Second attempt was to check for WM_SETFOCUS with ON_WM_SETFOCUS() and keep the previous wnd and cast it and add text, but that just changes the text on my dialog.
Third attempt was to move this to my CWnd but as far as I can see, WM_SETFOCUS is never called.
Edit:
Tried ON_WM_ACTIVATE with ::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized) inside my CWnd.
But that's not being called either.
Anybody has an idea what to try next?
You just answered yourself. The correct way to do it is: on the function that handles the
ON_COMMAND(...)
of each button call
pEdit->SetWindowText(_T("text"));.
GetFocus() is wrong, because it will return the button, as when you clicked it, you just finished to put the focus on it. You can get the edit using
CEdit* pEdit= ( CEdit*) GetDlgItem(ID_OF_EDIT);
where ID_OF_EDIT is the value you passed to CreateEx as iId parameter.

How do I set the checkmark next to a menu item in MFC?

I have a menu bar across the top of my dialog and one of the options is "mode" which contains "normal" and "debug". I'm trying to make it so that when the user clicks either of these two options, a checkmark will appear next to the last selected item in the dropdown menu.
This is what I found from searching around google, but I can't get it to work:
//event handler for user clicking on mode then normal in the menu
void CNew_RGB_ControlDlg::OnModeNormal()
{
//check the normal option when the user selects normal mode in the menu
CMenu menu;
menu.LoadMenu(IDR_MENU1);
menu.CheckMenuItem(ID_MODE_NORMAL, MF_CHECKED | MF_BYCOMMAND); //returns 8
menu.CheckMenuItem(ID_MODE_DEBUG, MF_UNCHECKED | MF_BYCOMMAND);//returns 0
}
I also have another of these functions for when debug is clicked, its the same code just the checked and unchecked are switched.
The return values make it seem like it should be working according to MSDN, but the menu items never change.
I have also tried this:
void CNew_RGB_ControlDlg::OnModeNormal()
{
CMenu menu;
menu.LoadMenu(IDR_MENU1);
menu.GetSubMenu(1)->CheckMenuItem(0, MF_BYPOSITION|MF_CHECKED);
menu.GetSubMenu(1)->CheckMenuItem(1, MF_BYPOSITION|MF_CHECKED);
}
What am I doing wrong? What do I need to do to make this work?
Instead of loading a fresh menu when an item is selected, you would need to get the current menu used in the dialog, like
CMenu *pMenu = GetMenu();
if (pMenu != NULL)
{ pMenu->CheckMenuItem(ID_MODE_NORMAL, MF_CHECKED | MF_BYCOMMAND);
pMenu->CheckMenuItem(ID_MODE_DEBUG, MF_UNCHECKED | MF_BYCOMMAND);
}
Declare your CMenu item as a member of the dialog class so it persists after you load the resource the first time, by declaring it in the handler and loading it you are operating on a different copy of the menu each time. You could also load it dynamically each time using ::GetMenu() as another response shows.
When you create the menu before displaying it you have to check the menu item you want and uncheck the others something like this:
// in CNew_RGB_ControlDlg.h
CMenu menu;
// in CNew_RGB_ControlDlg::OnInitDialog
// no need to get submenu if you use the menu id
menu.LoadMenu(IDR_MENU1);
menu.CheckMenuItem(ID_NORMAL, MF_CHECKED);
menu.CheckMenuItem(ID_DEBUG, MF_UNCHECKED);
}
When you respond to the command that set a different menu item as checked
switch(Command)
{
case ID_NORMAL:
menu.CheckMenuItem(ID_NORMAL, MF_CHECKED);
menu.CheckMenuItem(ID_DEBUG, MF_UNCHECKED);
break;
case ID_DEBUG:
menu.CheckMenuItem(ID_NORMAL, MF_UNCHECKED);
menu.CheckMenuItem(ID_DEBUG, MF_CHECKED);
break;
};
Any way to check/uncheck a menu item on the main menu bar?
My old code (from View Class)
CWnd* pParent = GetParent();
CMenu* pMenu = pParent->GetMenu();
pMenu->CheckMenuItem(ID_TEST_1, MF_UNCHECKED);
pMenu->CheckMenuItem(ID_TEST_2, MF_UNCHECKED);
No longer works in VC++ 2010. They say it has something to do with floating toolbars and/or active accessibility.
Nat Hager

C++ Win32 Creating a Popup Menu From Resource

I am trying to load a popup "right click" menu, and use the resource file to define the menu items. The picture shows what is happening when I right click, it displays room for 2 items, which is correct, but doesnt show any text.
In the .cpp:
POINT pt;
pt.x = LOWORD (lParam);
pt.y = HIWORD (lParam);
ClientToScreen (hwnd, &pt);
HMENU hMenu = LoadMenu(NULL, MAKEINTRESOURCE(IDR_POPUPMENU));
TrackPopupMenu (hMenu, TPM_RIGHTBUTTON, pt.x, pt.y, 0, hwnd, NULL);
and the resource:
IDR_POPUPMENU MENU DISCARDABLE
BEGIN
MENUITEM "test", IDM_TEST
MENUITEM "Close", IDM_CLOSE
END
any idea on what I am donig wrong?
Thanks.
EDIT: I just tested, and clicking in the "no text displayed" areas, and it sends the correct message. What could be causing it to not display the text?
Found the solution:
HMENU hMenu = LoadMenu(NULL, MAKEINTRESOURCE(IDR_POPUPMENU));
hMenu = GetSubMenu(hMenu, 0);
and resource:
IDR_POPUPMENU MENU DISCARDABLE
BEGIN
POPUP "TEST"
BEGIN
MENUITEM "Test", IDM_TEST
MENUITEM "Close", IDM_CLOSE
END
END
Just had to start the resource entry with a beginning sub menu, TEST does not display, only its menu items do.
Your menu resource is incorrect. It must be a popupmenu.
eg:
IDR_MENU_TRAY MENU
BEGIN
POPUP "ContextMenu"
BEGIN
MENUITEM "ShowWindow", ID_POPUP_SHOWWINDOW
MENUITEM "Exit", ID_POPUP_EXIT
END
END
TrackPopupMenu first parameter is a handle to a submenu associated with an existing menu item.
You can see the examples here: http://msdn.microsoft.com/EN-US/library/ms647558(v=VS.85,d=hv.2).aspx