My application has a toolbar type menu. I sub-classed the window procedure to allow visual enhancement, which means my application is painting the menubar. When the application window is minimized and restored, the menu repaints just fine.
But, if the system colors are changed while the application window is minimized, then the menubar disappears when the application window is restored. I found that when system colors change while the application window is minimized, WM_PAINT
is not sent to the menubar after the window is restored.
I tried the following:
Force sending
WM_PAINT
to the menubar by callingInvalidateRect()
in response toWM_SIZE
, but it did not cause aWM_PAINT
to be sent.Force sending
WM_PAINT
to the menubar by callingRedrawWindow()
with theRDW_INTERNALPAINT
flag. It succeeded in sendingWM_PAINT
, and the procedure for painting the menu is called. But still the menu does not show.Restore the window when the system colors change, by calling
ShowWindow()
in response toWM_SYSCOLORCHANGE
. It worked.WM_PAINT
is sent to the menubar and the menu shows up. However, I think it is rude to cause my window to popup suddenly, interrupting whatever the user is doing with some other program.
Can anyone advise me how to keep the menubar painting correctly without restoring the window?
Below is how I tried to do it.
In the main window procedure:
LRESULT CALLBACK DlgWndProc (HWND hDlg,UINT msg,WPARAM wParam,LPARAM lParam)
{
static int HighContrastOn ;
static HWND hwndMenuBar ;
static HINSTANCE hThisInst ;
static NMTOOLBAR nmtb = { NULL , IDC_MENUBAR , TBN_DROPDOWN , 0 } ;
static HIGHCONTRAST HighContrast = { sizeof (HIGHCONTRAST) , 0 , 0 } ;
switch (msg) {
case WM_CREATE :
hThisInst = ((LPCREATESTRUCT) lParam)->hInstance ;
SystemParametersInfo (SPI_GETHIGHCONTRAST,0,&HighContrast,0) ;
HighContrastOn = HighContrast.dwFlags & HCF_HIGHCONTRASTON ;
return 0 ;
case WM_DESTROY :
PostQuitMessage (0) ;
return 0 ;
case WM_CLOSE :
DestroyMenu (hMenu) ;
DestroyWindow (hDlg);
return 0 ;
case WM_SYSCOLORCHANGE :
ShowWindow (hDlg,SW_NORMAL) ;
SendMessage (hwndMenuBar,WM_SYSCOLORCHANGE,0,0) ;
return 0 ;
case WM_SETTINGCHANGE :
// Catch change in HighContrast status missed by WM_SYSCOLORCHANGE
SystemParametersInfo (SPI_GETHIGHCONTRAST,0,&HighContrast,0) ;
if (HighContrastOn == (HighContrast.dwFlags & HCF_HIGHCONTRASTON))
return 0 ;
HighContrastOn = HighContrast.dwFlags & HCF_HIGHCONTRASTON ;
SendMessage (hwndMenuBar,WM_SYSCOLORCHANGE,0,0) ;
return 0 ;
case WM_NOTIFY :
OnNotify (hDlg,wParam,lParam) ;
break ;
case WM_COMMAND :
OnCommand (hDlg,wParam,lParam) ;
break ;
case WM_INITMENUPOPUP :
int SubMenu ;
SubMenu = (int) SendMessage (hwndMenuBar,TB_GETHOTITEM,0,0) ;
for (int i = 0 ; i < 5 ; i++) {
if (GetSubMenu (hMenu,i) == (HMENU) wParam)
SendMessage (hwndMenuBar,WM_INITMENUPOPUP,SubMenu,0) ;
} /* for (int i = 0 ; i < 5 ; i++) */
InitMenuPopup ((HMENU) wParam,SubMenu) ;
return 0 ;
case WM_UNINITMENUPOPUP :
int i ;
// Find out whther the closing menu is sub menu or sub-sub menu
for (i = 0 ; i < 5 ; i++) {
if ((HMENU) wParam == GetSubMenu (hMenu,i))
break ;
} /* for (i = 0 ; i < 5 ; i++) */
if (i < 5) {
UnhookWindowsHookEx (DefaultMsgHook) ;
SendMessage (hDlg,WM_NOTIFY,IDC_MENUBAR,(LPARAM) &nmtb) ;
SendMessage (hwndMenuBar,WM_UNINITMENUPOPUP,0,0) ;
} /* if (i < 5) */
return 0 ;
case IDM_INIT :
InitDialog (hDlg) ;
hMenu = LoadMenu (hThisInst,"MyProgram") ;
hwndMenuBar = GetDlgItem (hDlg,IDC_MENUBAR) ;
nmtb.hdr.hwndFrom = hwndMenuBar ;
SetWindowSubclass (hwndMenuBar,MenuBarProc,0,0) ;
SendMessage (hwndMenuBar,WM_NULL,0,0) ;
return 0 ;
} /* switch (msg) */
/* Pass unprocessed messages to DefDlgProc */
return DefDlgProc (hDlg,msg,wParam,lParam) ;
} /* DlgWndProc */
LRESULT WINAPI MenuBarMsgHook (int Code,WPARAM wParam,LPARAM lParam)
{
static NMTOOLBAR nmtb ;
switch (Code) {
case MSGF_MENU :
#define Msg ((LPMSG) lParam)
HWND hwndMenuBar ;
hwndMenuBar = GetDlgItem (Msg->hwnd,IDC_MENUBAR) ;
switch (Msg->message) {
POINT pt ;
case WM_LBUTTONDOWN :
case WM_RBUTTONDOWN :
pt = Msg->pt ;
ScreenToClient (hwndMenuBar,(LPPOINT) &pt) ;
SendMessage (hwndMenuBar,Msg->message,wParam,
(LPARAM) MAKELONG (pt.x,pt.y)) ;
break ;
case WM_MOUSEMOVE :
pt = Msg->pt ;
ScreenToClient (hwndMenuBar,(LPPOINT) &pt) ;
SendMessage (hwndMenuBar,WM_MOUSEMOVE,wParam,
(LPARAM) MAKELONG (pt.x,pt.y)) ;
break ;
} /* switch (Msg->message) */
#undef Msg
} /* switch (Code) */
return CallNextHookEx (NULL,Code,wParam,lParam) ;
} /* MenuBarMsgHook */
LRESULT CALLBACK MenuBarProc (HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam,
UINT_PTR uIdSubclass,DWORD_PTR dwRefData)
{
static bool BtnDown ;
static int HighContrastOn ;
static int yc ;
static int xr ;
static int yb ;
static int HBtn = -1 ;
static char* Text[] = {"File","Edit","Settings","Design","Help"} ;
static RECT Rect[5] ;
static HFONT Font ;
static HPEN Pen ;
static HPEN HPen ;
static HPEN SPen ;
static HBRUSH Brush ;
static HBRUSH HBrush ;
static HBRUSH SBrush ;
static COLORREF BtnTextColor = GetSysColor (COLOR_BTNTEXT) ;
static COLORREF MenuColor = GetSysColor (COLOR_MENUBAR) ;
static COLORREF HotLightColor = GetSysColor (COLOR_HOTLIGHT) ;
static COLORREF HighLightColor = GetSysColor (COLOR_HIGHLIGHT) ;
static COLORREF HiLightTextColor = GetSysColor (COLOR_HIGHLIGHTTEXT) ;
static COLORREF WindowFrameColor = GetSysColor (COLOR_WINDOWFRAME) ;
static HIGHCONTRAST HighContrast = { sizeof (HIGHCONTRAST) , 0 , 0 } ;
static TRACKMOUSEEVENT tme = {sizeof (TRACKMOUSEEVENT),TME_LEAVE,NULL,
HOVER_DEFAULT} ;
HDC hDC ;
switch (msg) {
case WM_NULL :
SystemParametersInfo (SPI_GETHIGHCONTRAST,0,&HighContrast,0) ;
HighContrastOn = HighContrast.dwFlags & HCF_HIGHCONTRASTON ;
Font = CreateMenuFont (hwnd) ;
Pen = (HPEN) GetStockObject (NULL_PEN) ;
Brush = CreateSolidBrush (MenuColor) ;
if (HighContrastOn) {
HPen = CreatePen (PS_SOLID,0,WindowFrameColor) ;
SPen = CreatePen (PS_SOLID,0,WindowFrameColor) ;
HBrush = CreateSolidBrush (HighLightColor) ;
SBrush = CreateSolidBrush (HighLightColor) ;
} /* if (HighContrastOn) */
else {
HPen = CreatePen (PS_SOLID,0,0xF87400) ;
SPen = CreatePen (PS_SOLID,0,HotLightColor) ;
HBrush = CreateSolidBrush (0xFFF4E6) ;
SBrush = CreateSolidBrush (0xFED8B0) ;
} /* else */
for (int Btn = 0 ; Btn < 5 ; Btn++)
SendMessage (hwnd,TB_GETRECT,IDM_FILEMENU + Btn,(LPARAM) (Rect + Btn)) ;
hDC = GetDC (hwnd) ;
TEXTMETRIC tm ;
GetTextMetrics (hDC,&tm) ;
ReleaseDC (hwnd,hDC) ;
yc = (Rect[0].bottom - Rect[0].top - tm.tmHeight) / 2 + tm.tmAscent - 1 ;
xr = Rect[4].right ;
yb = Rect[0].bottom ;
tme.hwndTrack = hwnd ;
return 0 ;
case WM_PAINT :
if (! GetUpdateRect (hwnd,NULL,false))
break ;
#define rc (Rect[Btn])
PAINTSTRUCT ps ;
hDC = BeginPaint (hwnd,&ps) ;
SelectObject (hDC,Font) ;
SelectObject (hDC,Pen) ;
SelectObject (hDC,Brush) ;
SetTextAlign (hDC,TA_BASELINE | TA_CENTER) ;
SetTextColor (hDC,BtnTextColor) ;
SetBkMode (hDC,TRANSPARENT) ;
for (int Btn = 0 ; Btn < 5 ; Btn++) {
Rectangle (hDC,rc.left,rc.top,rc.right,rc.bottom) ;
if (Btn == HBtn)
continue ;
int xc = (rc.right + rc.left) / 2 ;
ExtTextOut (hDC,xc,yc,ETO_NUMERICSLATIN,&rc,Text[Btn],
(UINT) strlen (Text[Btn]),NULL) ;
} /* for (int Btn = 0 ; Btn < 5 ; Btn++) */
#undef rc
#define rc (Rect[HBtn])
if (HBtn > -1) {
SelectObject (hDC,BtnDown ? SPen : HPen) ;
SelectObject (hDC,BtnDown ? SBrush : HBrush) ;
SetTextColor (hDC,HighContrastOn ? HiLightTextColor : BtnTextColor) ;
RoundRect (hDC,rc.left + 1,rc.top + 1,rc.right - 1,rc.bottom - 2,2,2) ;
int xc = (rc.right + rc.left) / 2 ;
ExtTextOut (hDC,xc,yc,ETO_NUMERICSLATIN,&rc,Text[HBtn],
(UINT) strlen (Text[HBtn]),NULL) ;
} /* if (HBtn > -1) */
#undef rc
EndPaint (hwnd,&ps) ;
return 0 ;
case WM_SYSCOLORCHANGE :
DeleteObject (HPen) ;
DeleteObject (SPen) ;
DeleteObject (Brush) ;
DeleteObject (HBrush) ;
DeleteObject (SBrush) ;
HighLightColor = GetSysColor (COLOR_HIGHLIGHT) ;
WindowFrameColor = GetSysColor (COLOR_WINDOWFRAME) ;
HotLightColor = GetSysColor (COLOR_HOTLIGHT) ;
HiLightTextColor = GetSysColor (COLOR_HIGHLIGHTTEXT) ;
BtnTextColor = GetSysColor (COLOR_BTNTEXT) ;
MenuColor = GetSysColor (COLOR_MENUBAR) ;
SystemParametersInfo (SPI_GETHIGHCONTRAST,0,&HighContrast,0) ;
HighContrastOn = HighContrast.dwFlags & HCF_HIGHCONTRASTON ;
if (HighContrastOn) {
HPen = CreatePen (PS_SOLID,0,WindowFrameColor) ;
SPen = CreatePen (PS_SOLID,0,WindowFrameColor) ;
HBrush = CreateSolidBrush (HighLightColor) ;
SBrush = CreateSolidBrush (HighLightColor) ;
} /* if (HighContrastOn) */
else {
HPen = CreatePen (PS_SOLID,0,0xF87400) ;
SPen = CreatePen (PS_SOLID,0,HotLightColor) ;
HBrush = CreateSolidBrush (0xFFF4E6) ;
SBrush = CreateSolidBrush (0xFED8B0) ;
} /* else */
Brush = CreateSolidBrush (MenuColor) ;
return 0 ;
case WM_NCDESTROY:
DeleteObject (Font) ;
DeleteObject (HPen) ;
DeleteObject (SPen) ;
DeleteObject (Brush) ;
DeleteObject (HBrush) ;
DeleteObject (SBrush) ;
RemoveWindowSubclass (hwnd,MenuBarProc,0) ;
break ;
case WM_INITMENUPOPUP :
HBtn = (int) wParam ;
BtnDown = true ;
InvalidateRect (hwnd,NULL,true) ;
break ;
case WM_UNINITMENUPOPUP :
HBtn = -1 ;
BtnDown = false ;
InvalidateRect (hwnd,NULL,true) ;
break ;
case WM_MOUSEMOVE :
#define rc (Rect[Btn])
int x ;
int y ;
x = LOWORD (lParam) ;
y = HIWORD (lParam) ;
if (HBtn > -1 && x > xr && ! BtnDown) {
HBtn = -1 ;
InvalidateRect (hwnd,NULL,true) ;
break ;
} /* if (HBtn > -1 && x > xr && ! BtnDown) */
for (int Btn = 0 ; Btn < 5 ; Btn++) {
if (y < rc.bottom && x > rc.left && x < rc.right) {
if (Btn == HBtn)
break ;
HBtn = Btn ;
InvalidateRect (hwnd,NULL,true) ;
break ;
} /* if (y < rc.bottom && x > rc.left && x < rc.right) */
} /* for (int Btn = 0 ; Btn < 5 ; Btn++) */
TrackMouseEvent (&tme) ;
#undef rc
break ;
case WM_MOUSELEAVE :
if (! BtnDown) {
HBtn = -1 ;
InvalidateRect (hwnd,NULL,true) ;
} /* if (! BtnDown) */
return 0 ;
} /* switch (msg) */
return DefSubclassProc (hwnd,msg,wParam,lParam) ;
} /* MenuBarProc */
Here is an extract of fn. InitDialog() showing initialization of menubar
#define NUMBUTTONS 5
static TBBUTTON tbButtons[NUMBUTTONS] =
{{I_IMAGENONE, IDM_FILEMENU , TBSTATE_ENABLED, BTNS_DROPDOWN | BTNS_AUTOSIZE, {0}, 0,(INT_PTR) " File"} ,
{I_IMAGENONE, IDM_EDITMENU , TBSTATE_ENABLED, BTNS_DROPDOWN | BTNS_AUTOSIZE, {0}, 0,(INT_PTR) " Edit"} ,
{I_IMAGENONE, IDM_SETTINGSMENU,TBSTATE_ENABLED, BTNS_DROPDOWN | BTNS_AUTOSIZE, {0}, 0,(INT_PTR) " Settings"} ,
{I_IMAGENONE, IDM_DESIGNMENU , TBSTATE_ENABLED, BTNS_DROPDOWN | BTNS_AUTOSIZE, {0}, 0,(INT_PTR) " Design"} ,
{I_IMAGENONE, IDM_HELPMENU , TBSTATE_ENABLED, BTNS_DROPDOWN | BTNS_AUTOSIZE, {0}, 0,(INT_PTR) " Help"}} ;
HFONT MenuFont = CreateMenuFont (hwndMenuBar) ;
SendMessage (hwndMenuBar,WM_SETFONT,(WPARAM) MenuFont,false) ;
SendMessage (hwndMenuBar,TB_BUTTONSTRUCTSIZE,(WPARAM) sizeof(TBBUTTON),0) ;
SendMessage (hwndMenuBar,TB_ADDBUTTONS, (WPARAM) NUMBUTTONS,
(LPARAM) (LPTBBUTTON) &tbButtons) ;
SendMessage (hwndMenuBar,TB_AUTOSIZE,0,0) ;
SendMessage (hwndMenuBar,TB_SETINDENT,2,0) ;
ShowWindow (hwndMenuBar,SW_SHOW) ;
SetWindowSubclass (hwndMenuBar,MenuBarProc,0,0) ;
SendMessage (hwndMenuBar,WM_NULL,0,0) ;
DeleteObject (MenuFont) ;
The menubar is defined in the dialog box resource as follows
CONTROL " ",IDC_MENUBAR,"ToolbarWindow32",TBSTYLE_FLAT | TBSTYLE_LIST | WS_CLIPCHILDREN,0,0,0,0,0,HIDC_MENUBAR
You should forward the
WM_SYSCOLORCHANGE
message to the toolbar control.Have you sent
TB_AUTOSIZE
message?