I'm using the CMFCPopupMenu to create a right click popup menu. The problem is that the first time the menu is shown only the menu frame with shades is shown but the contents is white. The second time the menu is shown there are no problems. The code looks like this:
CPoint point;
::GetCursorPos (&point);
CMFCPopupMenu* pop = new CMFCPopupMenu();
pop->InsertItem(CMFCToolBarMenuButton(ID_COMMAND_1,NULL,-1,_T("Command 1")));
pop->InsertItem(CMFCToolBarMenuButton(ID_COMMAND_2,NULL,-1,_T("Command 2")));
pop->InsertItem(CMFCToolBarMenuButton(ID_COMMAND_3,NULL,-1,_T("Command 3")));
pop->InsertItem(CMFCToolBarMenuButton(ID_COMMAND_4,NULL,-1,_T("Command 4")));
pop->Create(this,point.x,point.y,NULL,0,true);
The parent class is based on CDialogEx.
Thanks.
I don't understand why my approach doesn't work but I found a way around it by defining the menu in the ressource and do like this:
CMenu menu;
menu.LoadMenu(IDR_SESSION_MENU);
HMENU hMenu = menu.GetSubMenu (0)->Detach ();
CMFCPopupMenu* pMenu = theApp.GetContextMenuManager()->ShowPopupMenu(hMenu, point.x, point.y, this, TRUE);
That Works and the only problem is that it's a bit more complicated to have a menu with dynamic entries depending on state and selection.
CMFCPopupMenu* pPopupMenu = new CMFCPopupMenu;
if (pPopupMenu->Create(pWndOwner, point.x, point.y, NULL, FALSE, TRUE))
{
pPopupMenu->InsertItem(CMFCToolBarMenuButton(57645, NULL, -1, _T("Command 1")), -1);
pPopupMenu->InsertItem(CMFCToolBarMenuButton(57646, NULL, -1, _T("Command 2")), -1);
pPopupMenu->RecalcLayout();
}
Need call RecalcLayout() after insert
Related
I would like to have dynamic menu in my application. This dynamic menu should contain popupmenus which will be added and removed on the fly. For the first approach I made dynamic menu created with single menu items.
AppendMenu(menu, MF_STRING, item_id, "TEST");
I have created algorithm which generate item_id and store them in array, so I could remove them by
DeleteMenu(menu, id_to_be_deleted, MF_BYCOMMAND);
I do not see any pitfalls of this implementation and I am happy with it. But then stuck with final implementation. I would like my menus would be popup menus.
new_popup_menu = CreatePopupMenu();
AppendMenu(new_popup_menu, MF_STRING, 1, "TEST1");
AppendMenu(new_popup_menu, MF_STRING, 2, "TEST2");
AppendMenu(new_popup_menu, MF_STRING, 3, "TEST3");
AppendMenu(menu, MF_STRING|MF_POPUP,
(UINT_PTR)new_popup_menu, "dynamic menu");
This code works as expected, but I have no idea how to remove "new_popup_menu" from "menu" since the "UINT_PTR uIDNewItem" parameter of AppendMenu now is used as handle to submenu, not ID and cannot be used with DeleteMenu+MF_BYCOMMAND.
Is there any way to remove this submenu item other then DeleteMenu+MF_BYPOSITION?
Is there a way to get menu item position by handle which is returned by CreatePopupMenu())?
I feel implementation algorithm of tracking which menu is on which position is pain in the ass. Since Windows has API to insert the menu after other specific menu, recreating whole menu tree is a waste of CPU time.
If you want to create a menu item that opens a submenu and has an ID then create it with InsertMenuItem(...) rather than AppendMenu(...). InsertMenuItem(...) lets you fill in a struct that specifies all of the properties you want to be set on the menu item you are creating, including ID and submenu. A lot of Win32 works this way: AppendMenu(...) is like a shorthand version for the more verbose version of the same function. When you run into situations in which you can't do something reasonable with a certain Win32 call, look for a synonymous call that takes a *INFO structure.
Code below:
...
HMENU menu_bar = GetMenu(hWnd);
HMENU new_menu = CreateMenu();
AppendMenu(menu_bar, MF_POPUP, (UINT_PTR)new_menu, "foobar");
AppendMenu(new_menu, MF_ENABLED | MF_STRING, 1002, "item1");
AppendMenu(new_menu, MF_ENABLED | MF_STRING, 1003, "item2");
HMENU dynamic_popup = CreatePopupMenu();
AppendMenu(dynamic_popup, MF_ENABLED | MF_STRING, 1004, "mumble");
AppendMenu(dynamic_popup, MF_ENABLED | MF_STRING, 1005, "quux");
// Below will add an item named "dynamic menu" to the end of new_menu
// that has an ID of 1006.
MENUITEMINFO mii = { 0 };
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_SUBMENU | MIIM_STRING | MIIM_ID;
mii.dwTypeData = (LPSTR)"dynamic menu";
mii.hSubMenu = dynamic_popup;
mii.wID = 1006;
InsertMenuItem(new_menu, 0, FALSE, &mii);
//DeleteMenu(new_menu, 1006, MF_BYCOMMAND);
...
I need to make tooltip a little bit right and lower to mouse cursor, but i can't do it in any way, tried different coordintaes but nothing seems to work. Where is the problem? Thank you.
// Add the new tooltip (if available)
if (m_LastToolTipRow!=-1 && m_LastToolTipRow!=-1)
{
// Not using CToolTipCtrl::AddTool() because it redirects the messages to CListCtrl parent
TOOLINFO ti = {0};
ti.cbSize = sizeof(TOOLINFO);
ti.uFlags = TTF_IDISHWND | TTF_TRANSPARENT; // Indicate that uId is handle to a control
ti.uId = (UINT_PTR)m_hWnd; // Handle to the control
ti.hwnd = m_hWnd; // Handle to window to receive the tooltip-messages
ti.hinst = AfxGetInstanceHandle();
ti.lpszText = LPSTR_TEXTCALLBACK;
m_OwnToolTipCtrl.SendMessage(TTM_ADDTOOL, 0, (LPARAM) (LPTOOLINFO) &ti);
m_OwnToolTipCtrl.SendMessage(TTM_TRACKPOSITION, 0, (LPARAM)MAKELPARAM(pt.x + 100, pt.y + 100));
m_OwnToolTipCtrl.SendMessage(TTM_TRACKACTIVATE, true, (LPARAM)&ti);
m_OwnToolTipCtrl.Activate(TRUE);
//Multiline
m_OwnToolTipCtrl.SetMaxTipWidth(256);
//m_OwnToolTipCtrl.SetMaxTipWidth(SHRT_MAX);
}
TTF_IDISHWND
Indicates that the uId member is the window handle to the tool. If this flag is not set, uId is the tool's identifier.
According to this, the window with m_hWnd handle is the one that shows the tooltip and you can position the window itself. If you meant a tooltip separate to that window than there is a principal problem there.
I am using VC6.0. I am trying programming to show contextmenu when I right click on the item of the ListBox. But now the popmenu can show anywhere in the rect of ListBox, since I only can get the rect of the ListBox, and I dont know how to get the rect of the item. I know that there is a macro ListView_GetSubItemRect which seems to get the rect of item of ListView. Is there similar way for ListBox, or is there a way to get the width and the height of item of ListBox, so I can caculate the rect? I didnt find some useful information on msdn and google? Can anyone give me some ideas? Thanks.
My current Code:
void My_OnContextMenu(HWND hwnd, HWND hwndContext, UINT xPos, UINT yPos)
{
HWND hList = GetDlgItem(hwnd,IDC_LIST_RESTYPE);
if (hList == hwndContext)
{
if(-1!=indexLB)
{
RECT rect;
POINT pt;
GetClientRect(hwndContext, &rect);
ScreenToClient(hwndContext, &pt);
if(PtInRect(&rect, pt))
{
HMENU hroot = LoadMenu((HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE), MAKEINTRESOURCE(IDR_MENU_DELTYPE));
if(hroot)
{
HMENU hpop = GetSubMenu(hroot,0);
ClientToScreen(hwndContext, &pt);
TrackPopupMenu(hpop, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, 0, hwndContext, NULL);
DestroyMenu(hroot);
}
}
}
}
}
Edit
Current:
First, I left click an item to selected、 an item. And Second I right click the selected item to show popmenu. It shows normally. But in the second step if I click the blank area of ListBox, it shows menu either. That is not what I expected.
What I expected is:
The menu only shows when I click an item and the position only over the item. When I right click other area, it wont be showed.
You are looking for the ListBox_GetItemRect macro.
However, I do feel that the user will find it odd to click in one place and see the menu appear somewhere else.
The proper solution to this problem is to popup the context menu at the mouse position. Clicking in one place and popping it up somewhere else would be very bad.
To get the mouse position use GetCursorPos().
http://msdn.microsoft.com/en-us/library/windows/desktop/ms648390%28v=vs.85%29.aspx
To be clear, first use ListBox_GetItemRect to work out which item is clicked on, and ignore it if none. Then use GetCursorPos so the menu appears exactly where the mouse is -- inside the list item -- and not somewhere a few pixels away. The Windows UI standards are that the context menu appears at the cursor position.
I'm not sure Why you wrote your own OnContextMenu - you should use the class wizard to map WM_CONTEXTMENU with the standard handler where the existing function ends up in your code like this:
//Wizard Added this the message map block
ON_WM_CONTEXTMENU()
//Declares the function with the proper parameters
void MyDlg::OnContextMenu(CWnd* pWnd, CPoint point);
//in the body of OnContextMenu use the system supplied parameters and the
//menu will appear next to the mouse position wherever it is clicked in the control
CMenu popupmenu;
popupmenu.LoadMenu(IDR_RMOUSEPOPUP);
int Command = (int)popupmenu.GetSubMenu(0)->TrackPopupMenu(
TPM_LEFTALIGN | TPM_BOTTOMALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD | TPM_NONOTIFY,
point.x, point.y, pWnd);
I have an EDIT control created like this:
hwndEDIT_5 = CreateWindowEx (
0, "EDIT", NULL,
WS_VSCROLL | WS_BORDER | WS_VISIBLE | WS_CHILD | ES_MULTILINE | ES_READONLY,
135, 450, 555, 200,
h2, ( HMENU ) ID_EDIT_CONSOLE,
h1, NULL
);
As you can see it is a read-only EDIT area where multi lines text can be displayed. It is supposed to be a console where I can display some information for users when they use the program. I would like the text area to automatically scroll to the bottom-most entry (the newest one) whenever a new line (or message for an user) is added. I've implemented this:
SetDlgItemText ( h2, ID_EDIT_CONSOLE, ch_s );
SCROLLINFO scr;
SCROLLINFO * scr_p = &scr;
scr.cbSize = sizeof ( SCROLLINFO );
scr.fMask = SIF_RANGE;
GetScrollInfo ( GetDlgItem ( h2, ID_EDIT_CONSOLE), SB_VERT, scr_p );
int mmax = scr.nMax;
scr.fMask = SIF_POS;
scr.nPos = mmax;
SetScrollInfo ( GetDlgItem ( h2, ID_EDIT_CONSOLE), SB_VERT, scr_p, TRUE );
That code is scrolling vertical scrollbar to the end of an EDIT control after adding new msg and it works great, the scrollbar gets scrolled but the text still remains visible from the beginning - it rewinds to the beginning after addition while scrollbar rewinds to the bottom. How to make it properly?
Last but not least - this is might be important - in order to display a message firstly I capture the text that is already displayed by using:
GetDlgItemText ( h2, ID_EDIT_CONSOLE, buf, len + 1 );
then I convert buf into string and add to that string a new message that I want to display. Then I convert it back to char array and set it up with SetDlgItemText. I seperate lines by using \r\n. I've coded it that way because I didn't know how to add a line to an EDIT control in different way than using SetDlgItemText. And it adds only one entry AFAIK - if used twice I will not come up with two entries added to an EDIT control, but the first one will get replaced by second function call.
Don't use SetScrollInfo. Use SendMessage() with the EM_LINESCROLL message, sending the message to the edit control's window handle.
SendMessage(MemoHwnd, EM_LINESCROLL, 0, NumLinesToScroll);
The documentation says:
The control does not scroll vertically past the last line of text in the edit control. If the current line plus the number of lines specified by the lParam parameter exceeds the total number of lines in the edit control, the value is adjusted so that the last line of the edit control is scrolled to the top of the edit-control window.
I had the same problem and solved it with Jerry Coffin's answer and some research.
This is the way I use now:
string text = "Append this text";
SendMessageA(hEdit, EM_SETSEL, 0, -1); //Select all
SendMessageA(hEdit, EM_SETSEL, -1, -1);//Unselect and stay at the end pos
SendMessageA(hEdit, EM_REPLACESEL, 0, (LPARAM)(text.c_str())); //append text to current pos and scroll down
If needed: To scroll at the end of Edit Control without appending text:
SendMessageA(hEdit, EM_SETSEL, 0, -1); //Select all.
SendMessageA(hEdit, EM_SETSEL, -1, -1);//Unselect and stay at the end pos
SendMessageA(hEdit, EM_SCROLLCARET, 0, 0); //Set scrollcaret to the current Pos
You can add text by setting the beginning and end of the selection to the end of the text in the control (EM_SETSEL), then replacing the (empty) selection with your new text (EM_REPLACESEL).
Scrolling to the bottom can be done with EM_SCROLLCARET after the caret (the selection) is at the end of the text. There are other ways, but if you're doing it immediately after adding text, this is probably the easiest.
in my case I had a multi line string and Ken White's idea worked very well:
HWND hEdit = this->GetDlgItem(IDC_EDIT_LOG)->m_hWnd;
if (hEdit)
{
int lineCount = m_strClientLog.Replace(_T("\n"), _T("\n"));
::SendMessage(hEdit, EM_LINESCROLL, 0, lineCount);
}
for MFC projects you can use:
mLoggingTextCtl.SendMessage(EM_SETSEL, 0, -1); //Select all.
mLoggingTextCtl.SendMessage(EM_SETSEL, -1, -1);//Unselect and stay at the end pos
mLoggingTextCtl.SendMessage(EM_SCROLLCARET, 0, 0); //Set scrollcaret to the current Pos
I just started to use dialogs and I really like the possibility to define the layout in resource file. But is it possible to set up one dialog and embed it into another (i.e., no floating dialogs)?
With plain windows, I created the main window with one child window. Then, I created even more windows (like "edit", "static", ...) and added them to the child. I did so in order to group those several windows in to one window so I can control, say, the visibility of all edits and statics easily. Kind of like grouping (but it doesn't have the border of GroupBox).
Is it possible to rewrite the above, but with dialogs written down in .rc file?
I'm using plain C and Win32.
Example of what I did:
main = CreateWindow(...);
container = CreateWindow(... hWndParent = main ...);
label = CreateWindow("static", ... container);
edit = CreateWindow("edit", ... container);
Now, if I can hide or resize both label and edit just but controlling container.
Example of what I would like to have:
MAIN_DIALOG DIALOG 10, 20, 30, 40 STYLE ...
BEGIN
CONTROL "container" ...
END
How do I add 'label' and 'edit' to "container" control?
Also, in the resource editor set the dialog style to 'child' and border to 'none'.
What you want to do is probably a little bit similar to tabbed dialogs. There some controls are embedded from separate resources with a outer dialog. You can then show/hide all the controls within a tab by calling ShowWindow just for the subdialog:
In you main dialog Callback add something like
HWND SubDlgHwnd; // Global or probably within a struct/array etc.
case WM_INITDIALOG:
{
HRSRC hrsrc;
HGLOBAL hglobal;
hrsrc = FindResource(sghInstance, MAKEINTRESOURCE(SubDlgResId), RT_DIALOG);
hglobal = ::LoadResource(sghInstance, hrsrc);
SubDlgHwnd = CreateDialogIndirect(sghInstance, (LPCDLGTEMPLATE)hglobal, hDlg, ChildDialogCallback);
SetWindowPos(SubDlgHwnd, HWND_TOP, x, y, 0, 0, SWP_NOSIZE);
break;
}
case WM_COMMAND:
{
...
if(UpdateVisibility)
ShowWindow(SubDlgHwnd, showSubDialog ? SW_SHOW : SW_HIDE);
}
This might be a good Startpoint for Microsofts documentation.
You also have to add DS_CONTROL style to the dialog(s) you want to embed. Without it embedded dialog window will be shown with window header what is hardly one wants to.