Menubar disappears when system colors are changed while the window is minimized

164 Views Asked by At

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:

  1. Force sending WM_PAINT to the menubar by calling InvalidateRect() in response to WM_SIZE, but it did not cause a WM_PAINT to be sent.

  2. Force sending WM_PAINT to the menubar by calling RedrawWindow() with the RDW_INTERNALPAINT flag. It succeeded in sending WM_PAINT, and the procedure for painting the menu is called. But still the menu does not show.

  3. Restore the window when the system colors change, by calling ShowWindow() in response to WM_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
2

There are 2 best solutions below

4
On

You should forward the WM_SYSCOLORCHANGE message to the toolbar control.

case WM_SYSCOLORCHANGE:
    SendMessage( toolbar_hwnd, WM_SYSCOLORCHANGE, wparam, lparam );
    break;

Have you sent TB_AUTOSIZE message?

SendMessage( toolbar_hwnd, TB_AUTOSIZE, 0, 0 );
0
On

Thanks to Daniel Sek, I added the line he suggested in response to WM_SIZE and it solved the problem.

 case WM_SIZE :
     SendMessage (hwndMenuBar,TB_AUTOSIZE,0,0) ;
     return 0 ;

I did not need to call InvalidateRect () for the MenuBar. TB_AUTOSIZE msg caused WM_PAINT msg to be sent to the MenuBar and showed the menu.

Many thanks for your support.