Add Window Close Button To Menu Bar -- C++ - c++

I am trying to write an application that does not have a title/caption bar (a gross waste of screen real estate). So, I wish to add a close button ("X") to the end of the menu bar. Here is the code that I have tried:
mII.cbSize = sizeof(MENUITEMINFO);
mII.fMask = MIIM_FTYPE | MIIM_BITMAP;
mII.fType = MFT_BITMAP | MFT_RIGHTJUSTIFY;
mII.hbmpItem = HBMMENU_MBAR_CLOSE;
InsertMenuItem(hMenu, NUMMI, TRUE, &mII);
DrawMenuBar(hwnd);
In this case, "NUMMI" is equal to 5, the current number of main-menu items (numbered '0' through '4'). The new close button would be item number '5'.
However, the code seems to do nothing! No button appears on the menu bar. Am I missing something?

Thanks Remy. I eliminated the MFT_BITMAP flag, and now, it works. The MS documentation did not make this clear.
For those who might be reading this, asking the same question, I should note, that the above code only displays the button. Functionality must be added, just like any other button or menu item. A unique identifier is assigned to the wID member of the structure. The command is handled in the WM_COMMAND case, along with the other menu items.
Here is the revised, working code:
mII.cbSize = sizeof(MENUITEMINFO);
mII.fMask = MIIM_FTYPE | MIIM_BITMAP;
mII.fType = MFT_RIGHTJUSTIFY;
mII.wID = ID_MENUBAR_CLOSE;
mII.hbmpItem = HBMMENU_MBAR_CLOSE;
InsertMenuItem(hMenu, NUMMI, TRUE, &mII);
DrawMenuBar(hwnd);
ID_MENUBAR_CLOSE is a macro, defining a unique number that I assigned to this button. The command is the handled in the WM_COMMAND case:
case ID_MENUBAR_CLOSE:
SendMessage(hwnd, WM_CLOSE, 0, 0L); return 0;
return 0;
Voila, a working close button on the menu bar.

Related

Common Dialogboxes Win32

I have a common color dialog box that I want to activate on a menu press.
Unfortunately, every time I press on the item, the window loses focus, as if a dialog box is coming up, but the dialog box never shows up.
The code I'm using right now is below:
case ID_TOOL_CHOOSECOLOR:
//show colour dialog
ChooseColor(&cc);
and I've initialized my CHOOSECOLOR structure like this:
cc.lStructSize = sizeof (CHOOSECOLOR) ;
cc.hwndOwner = NULL ;
cc.hInstance = NULL ;
cc.rgbResult = RGB (0x80, 0x80, 0x80) ;
cc.lpCustColors = crCustColor ;
cc.Flags = CC_RGBINIT | CC_FULLOPEN ;
cc.lCustData = 0 ;
cc.lpfnHook = NULL ;
cc.lpTemplateName = NULL ;
What's weird is, the dialog box only shows up after I press the "ALT" key (and only the alt key). Any tips?
I'm trying to do this in an MDI document by the way.
Thanks
The only problem I can see is that you did not specify an owner for the dialog. This could lead to the dialog showing behind your main window. Specify the handle of your main window to be the dialog's owner.
Read more about window ownership on the Window Features page on MSDN.
Fixed it!
Turns out, the issue I had was in the main window's WndProc.
I had set my WM_PAINT command to return 0; instead of break;.
Changing my return 0 to break solved everything!
I think this is cause my return would exit the wndproc, whereas break would allow me to continue to the return MDIFrameProc(hwnd, message, wparam, lparam) .
I maybe wrong, but that's the best explanation that I can come up with that explains why break works but return 0 doesn't.
After you paint, make sure you call ValidateRect(HWND,CONST RECT*);
// https://learn.microsoft.com/en-us/windows/win32/dlgbox/color-dialog-box
// http://winapi.freetechsecrets.com/win32/WIN32Choosing_a_Color.htm
COLORREF acrCustClr[16]; // array of custom colors
CHOOSECOLOR cc = {sizeof(cc)};
ZeroMemory(&cc, sizeof(CHOOSECOLOR));
cc.lStructSize = sizeof(CHOOSECOLOR);
cc.hwndOwner = hwnd;
cc.Flags = CC_FULLOPEN | CC_RGBINIT;
cc.lpCustColors = (LPDWORD) acrCustClr;
BOOL fOk = ChooseColor(&cc);
if (fOk) {
}

Modifying menu items

I have a popup menu I would like to modify before it is being displayed. I can actually modify the string of a menu item fine. The problem is, that this renders it useless as nothing happens when the modified menu item is clicked on.
CMenu* pPopup = menu.GetSubMenu(0);
ASSERT(pPopup != NULL);
CWnd* pWndPopupOwner = this;
while(pWndPopupOwner->GetStyle() & WS_CHILD)
pWndPopupOwner = pWndPopupOwner->GetParent();
// modify string
pPopup->ModifyMenu(1, MF_BYPOSITION | MF_STRING, NULL, oss.str().c_str());
pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, pWndPopupOwner);
Your call to ModifyMenu is setting the menu's ID to zero (via the third argument). You need to keep that ID the same.
If that's awkward, the SetMenuItemInfo API lets you change the string without changing the ID.

DialogBoxIndirect creates dialog bigger than asked

For my application I need to create a Dialog Box without using resource.
I am trying to do it with DialogBoxInderect function.
The code is unbelievably ugly but somehow I succeeded to do it.
The problem is that dialog, for some reason, is much bigger than I asked with much bigger fonts.
Here is how the dialog looks like if I load it from resource:
And here is the dialog with the same size stated when I call DialogBoxInderect function.
Here is how it is defined in code:
HGLOBAL hGlobal;
LPDLGTEMPLATE wlsDialogTemplate;
LPDLGITEMTEMPLATE wlsDialogItemTemplate;
LPWORD nextItem;
LPWSTR itemString;
int32_t itemStringLength;
// Check for memory allocation errors
hGlobal = GlobalAlloc(GMEM_ZEROINIT, 1024);
if (!hGlobal)
return -1;
wlsDialogTemplate = (LPDLGTEMPLATE)GlobalLock(hGlobal);
// Define a dialog box.
wlsDialogTemplate->style = WS_CAPTION;
wlsDialogTemplate->x = 0;
wlsDialogTemplate->y = 0;
wlsDialogTemplate->cx = 320;
wlsDialogTemplate->cy = 115;
GlobalUnlock(hGlobal);
retCode = DialogBoxIndirect(0, (LPDLGTEMPLATE)hGlobal, 0, ActivateWlsMsgDialog);
And here is how it is defined in RC file:
IDD_WLS_SMALL_MESSAGE_DLG DIALOGEX 0, 0, 320, 115
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION
CAPTION "Dialog"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
DEFPUSHBUTTON "OK",ID_CUSTOM_OK,175,95,120,15
PUSHBUTTON "Cancel",ID_CUSTOM_CANCEL,45,95,120,15
CTEXT "Static",IDC_HEADER_TEXT,120,10,170,70
CONTROL "",IDC_LOGO,"Static",SS_BITMAP,16,10,83,70
END
As you can see, the second dialog is much bigger than defined. I tried to play with various style flags but without any success (That is why there is red cross on the bigger dialog).
Any help with that?
Thanks!
The larger size is easy to explain. Windows automatically sizes the dialog in accordance with its font, and the larger dialog uses a different font. In fact, it is using the default system font (more info on the confusing issue of Windows dialog fonts is found in my answer here).
So the real issue to focus on is why it's using a different font, and fixing that will solve the size problem.
In the dialog box resource file, you specify the DS_SETFONT flag as one of the dialog box styles. According to the documentation, this flag
[i]ndicates that the header of the dialog box template (either standard or extended) contains additional data specifying the font to use for text in the client area and controls of the dialog box. If possible, the system selects a font according to the specified font data. The system passes a handle to the font to the dialog box and to each control by sending them the WM_SETFONT message.
So that explains why that one is displaying with the expected font.
The next logical question is what's different about your dynamically-created dialog template, shown with the DialogBoxIndirect function. The culprit is, once again, the DS_SETFONT flag, but in this case, the flag is absent. That means that the dialog doesn't contain any information about which font to use to display its controls, and the system defaults to the default system font (which is the ugly Windows 2.0-era font that you see in the second screenshot).
Once we've come to this understanding, the solution should be obvious: you need to tell the dialog which font you want it to use. There are two possible ways of doing so that come to mind:
You can set the DS_SETFONT flag and provide the font information in the header of the dialog box template as described in the above-linked documentation.
Or you can simply create and set the dialog's font in response to the WM_INITDIALOG message.
The latter is probably what you really want to do, as it allows you to use the actual system font (which, confusingly, is different from what I've been calling the "default" system font), which is Segoe UI in Windows Vista and later. Note that even in your first screenshot, it's using MS Sans Serif and therefore sticks out like a sore thumb in the Aero interface. Again, see this answer for more about fonts than you ever wanted to know and sample code for making this work. You'll need to make sure that you set the font for the dialog itself and all of its child controls.
I had played with DialogBoxIndirect (actually with DialogBoxIndirectParam), and here's the part of the code that sets the font. Observe the DS_SHELLFONT option.
LPWORD lpwAlign(LPWORD lpIn, int nAlignment)
{
return (LPWORD)(((ULONG_PTR)lpIn + nAlignment - 1) & -nAlignment);
}
LRESULT DisplayMyMessage(HINSTANCE hinst, HWND hwndOwner, LPMYMESSAGEPARAMS pParams)
{
WORD mem[1024]; // Buffer for dialog resource
LPDLGTEMPLATEW lpdt; // Pointer to heading resource structure
LPDLGITEMTEMPLATEW lpdit; // Pointer to current control
LPWORD lpw; // Cursor to resource buffer
LPWSTR lpwsz; // Cursor to resource buffer (of type WCHAR)
LPCWSTR lpwszCaption; // Aux pointer for text copying
LRESULT ret; // Function's return value
lpdt = (LPDLGTEMPLATEW)lpwAlign( mem, 4 );
//-----------------------
// Define a dialog box.
//-----------------------
lpdt->style = WS_POPUP | WS_BORDER | WS_SYSMENU | WS_CAPTION |
DS_MODALFRAME | DS_CENTER | DS_SHELLFONT;
lpdt->dwExtendedStyle = 0;
lpdt->cdit = 3; // number of controls
lpdt->x = 0; lpdt->y = 0;
lpdt->cx = 164; lpdt->cy = 49;
lpw = (LPWORD)(lpdt + 1);
// Dialog menu
*lpw++ = 0;
// Dialog class
*lpw++ = 0;
// Dialog title
for (lpwsz = (LPWSTR)lpw, lpwszCaption = L"Choose language";
*lpwsz++ = *lpwszCaption++;
);
lpw = (LPWORD)lpwsz;
// Dialog font
if ( lpdt->style & (DS_SETFONT | DS_SHELLFONT) )
{
// Size
*lpw++ = 8;
// Typeface name
for (lpwsz = (LPWSTR)lpw, lpwszCaption = L"MS Shell Dlg";
*lpwsz++ = *lpwszCaption++;
);
lpw = (LPWORD)lpwsz;
}
// Define the rest of the controls
...
ret = DialogBoxIndirectParamW( hinst, lpdt,
hwndOwner, MyMessageProc, (LPARAM)pParams );
return ret;
}
This can be solved in your dialog handler by looking for the WM_INITDIALOG message, and then setting the font for all the controls in the dialog.
int CALLBACK SetChildFont(HWND child, LPARAM font) {
SendMessage(child, WM_SETFONT, font, TRUE);
return TRUE;
}
static int CALLBACK MyMessageProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_INITDIALOG:
/* Set font for dialog and all child controls */
EnumChildWindows(hwnd, (WNDENUMPROC)SetChildFont, (LPARAM)GetStockObject(DEFAULT_GUI_FONT));
break;
}
return 0;
}

Scrollbar arrows do not get redrawn while thumbtrack gets redrawn correctly

I have created a custom control by registering a new class for it and instantiating it as a child to a top-level window. The control is basically a list. In order to save some effort, I decided to use the WS_VSCROLL window class in order to add scrollbars to my custom control.
My trouble is that when I resize the window, I proceed to recalculate the size of the thumbtrack and page size, etc. then call SetScrollInfo with the redraw variable set to true. This redraws the thumbtrack correctly, but the arrows do not get redrawn. Therefore, if I resize the window from below, the top arrow is still good, but the bottom one goes missing. If I resize to the right, then both arrows change position and so both disappear.
I can't seem to find a way to get a handle to the internal scrollbar control that WS_VSCROLL creates in order to call an invalidate on it or something. I really don't know how to proceed form here. One thing to keep in mind is that if I add CS_VREDRAW | CS_HREDRAW to the top level window, everything gets drawn correctly, so I know that at least the internal values are all fine. It's just that that way I get too much redrawing and flicker.
EDIT: I'm posting some code. I really wanted to avoid this because I'll probably be put to shame, but please know that probably no one but myself will ever use it.
The control is a column of tweets similar to how Tweetdeck works. Each tweet is also a custom control, but that paints just fine and I have no problems there. The column control itself does not respond to WM_PAINT at all because it just contains Tweet controls, which handle their own painting.
Here are also the window classes for each of these:
register class of top level is NULL and CreateWindow class is WS_OVERLAPPED | WS_CLIPCHILDREN.
register class of column is NULL and CreateWindow class is WS_CHILD | WS_VSCROLL.
register class of tweet is NULL and CreateWindow class is WS_CHILD.
Code:
LRESULT Column::OnResize(WPARAM wParam, LPARAM lParam) // responds to WM_SIZE
{
// global height var
lastWidth = width;
lastHeight = height;
width = LOWORD(lParam);
height = HIWORD(lParam);
//updateScrollPageSize();
SCROLLINFO si;
si.cbSize = sizeof(si);
si.fMask = SIF_ALL;
GetScrollInfo (hWnd, SB_VERT, &si);
// Resize all child tweets
if (lastWidth != width ||
lastHeight != height && si.nMax - si.nPos < height)
{
// C++0x lambdas can only take in local variables?!
int localWidth = width;
int *tweetBottomPtr = &lastTweetBottom;
*tweetBottomPtr = 0;
std::for_each(tweets.begin(), tweets.end(),
[localWidth, tweetBottomPtr, &si](TweetBox *tweet)
{
MoveWindow(tweet->getHandle(),
tweet->getX(), *tweetBottomPtr - si.nPos,
localWidth, tweet->getHeight(), TRUE);
*tweetBottomPtr += tweet->getHeight();
});
}
updateScrollPageSize();
UpdateWindow(hWnd);
return 0;
}
and
void Column::updateScrollPageSize()
{
SCROLLINFO si;
// Set the vertical scrolling range and page size
si.cbSize = sizeof(si);
si.fMask = SIF_ALL;
GetScrollInfo (hWnd, SB_VERT, &si);
si.fMask = SIF_PAGE | SIF_RANGE;
si.nMin = 0;
si.nMax = lastTweetBottom;
si.nPage = (int)((float)height / lastTweetBottom * si.nMax);
SetScrollInfo(hWnd, SB_VERT, &si, TRUE);
}
You could try sending an extra WM_NCPAINT message to the column in your WM_SIZE handler. But I thought that happens automatically.
But I find the solution proposed by BrendanMcK to make use of a default listbox the winner. Pity it's not an answer.
Implement your scroll bars on your parent class, Not your child class. This will prevent repaint messages to the child class from causing the child class to completely redraw it's self. Don't handle wm_paint messages on your parent class, only in your child class. This will allow windows to do default processing on the scroll bar. And both windows need to be CS_HREDRAW | CS_VREDRAW.

win32 select all on edit ctrl (textbox)

I am creating my textbox with these options. I can Copy / Cut / Paste / Undo, but when I hit Select All it doesn't select all. I can right click and click Select All but CTRL + A doesn't do anything. Why?
wnd = CreateWindow("EDIT", 0,
WS_CHILD | WS_VISIBLE | ES_MULTILINE | WS_HSCROLL | WS_VSCROLL | ES_AUTOHSCROLL | ES_AUTOVSCROLL,
x, y, w, h,
parentWnd,
NULL, NULL, NULL);
Ctrl+A is not a built-in accelerator like Ctrl+C and Ctrl+V. This is why you see WM_CUT, WM_PASTE and WM_COPY messages defined, but there is no WM_SELECTALL.
You have to implement this functionality yourself. I did in my MFC app like this:
static BOOL IsEdit( CWnd *pWnd )
{
if ( ! pWnd ) return FALSE ;
HWND hWnd = pWnd->GetSafeHwnd();
if (hWnd == NULL)
return FALSE;
TCHAR szClassName[6];
return ::GetClassName(hWnd, szClassName, 6) &&
_tcsicmp(szClassName, _T("Edit")) == 0;
}
BOOL LogWindowDlg::PreTranslateMessage(MSG* pMsg)
{
if(pMsg->message==WM_KEYDOWN)
{
if ( pMsg->wParam=='A' && GetKeyState(VK_CONTROL)<0 )
{
// User pressed Ctrl-A. Let's select-all
CWnd * wnd = GetFocus() ;
if ( wnd && IsEdit(wnd) )
((CEdit *)wnd)->SetSel(0,-1) ;
}
}
return CDialog::PreTranslateMessage(pMsg);
}
Note, I stole IsEdit from this page: http://support.microsoft.com/kb/145616
I point that out partly because I want to give credit, and partly because I think the IsEdit function (comparing classname strings) is dorky and I want to give blame.
You could simply use EM_SETSEL message to the textbox,
According to MSDN ,
If the start is 0 and the end is –1, all the text in the edit control is selected. If the start is –1, any current selection is deselected.
so,
SendMessage(hwndEdit,EM_SETSEL,0,-1);
Will work fine.
I tend to use MFC (forgive me) instead of Win32 so I cannot answer this definitively, but I noticed this comment added to a page on an MS site concerning talking with an Edit control (a simple editor within the Edit control):
The edit control uses WM_CHAR for
accepting characters, not WM_KEYDOWN
etc. You must Translate() your
messages or you ironically won't be
able to edit the text in the edit
control.
I don't know if this applies to BoltBait's response, but I suspect it does.
(I found this at http://msdn.microsoft.com/en-us/library/bb775462(VS.85).aspx)
You need to capture that keystroke and do the select all yourself.
Here is some C# code for use with a RichTextBox:
protected override void OnKeyDown(KeyEventArgs e)
{
// Ctrl-A does a Select All in the editor window
if (e.Control && (e.KeyCode == Keys.A))
{
this.SelectAll();
e.Handled = true;
}
}
Sorry, I don't have Win32 code for you.
The strange thing is that Ctrl+A DOES work (as select all), if you do NOT specify ES_MULTILINE
But that doesn't help if you need multiline
The MSDN documentation for ES_MULTILINE doesn't appear to say anything about this.
Could it be that something else is stealing Ctrl+A? Use Spy++ to verify that it reaches your edit control.
Why not add an accelerator for Ctrl+a to SelectAll?