Changing Dialog Box/CFormView Background Color

Home
Back To Tips Page

Every once in a while there is a reason to change the background of a dialog or CFormView-derived window.  This is not quite as trivial as it might appear, but it is actually only a couple extra lines of code to do the job right.

The first-cut solution would be to create an OnEraseBkgnd handler in the dialog class, simply to paint the entire dialog the desired color.
BOOL CfvcolorView::OnEraseBkgnd(CDC* pDC)
{
    CRect r;
    GetClientRect(&r);
    if((HBRUSH)brush == NULL)
        brush.CreateSolidBrush(MYCOLOR);
    pDC->FillRect(&r, &brush);
    return TRUE;
} 

The result is a bit unfortunate using this simple approach.  The dialog comes out looking like the dialog on the left. 

In this example, I also show, top to bottom, left to right

  • A group box
  • Three CStatic controls as labels
  • A CButton which is a radio button
  • A CTreeCtrl
  • A CListBox
  • A CButton which is a check box
  • A CButton which is an ordinary button.
  • A CStatic which is a label
  • A CComboBox in the "simple" style
  • A CComboBox in the "Dropdown" style (the box shown is an edit control)
  • A CComboBox in the "DropList" style (the box shown is not editable)
  • A CStatic which is a label
  • A CListCtrl in icon mode
  • A CListCtrl in report mode
  • A CEdit control
  • A CRichEditCtrl
  • A CIPAddress control

 The Good News

  • The dialog background is the correct color

The Bad News

  • The static controls have the standard-dialog-box color on their background
  • The caption on the group box has the standard background color
  • The radio button has the standard background color
  • The check box has the standard background color

 

We'd like to get the background of those CStatic controls to look correct.  We could subclass each of the controls and have them do their own OnEraseBkgnd handlers, but the idea is to look for a solution that does not require creating new classes, or if for some reason these are already derived from other classes (and assuming those classes do not themselves implement OnEraseBkgnd handlers) the apparent solution is to create an OnCtlColor handler which will return a background brush no matter which of the child controls has generated a request.  The code would be
HBRUSH CfvcolorView::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
    {
     if((HBRUSH)brush == NULL)
        brush.CreateSolidBrush(MYCOLOR);
     return (HBRUSH) brush;
    } 

This looks good in that it will now change the background color of the controls, but unfortunately, it isn't quite that simple, because the result of that code is shown to the left.  Note that there are some changes.  But while part of the static control (the part to the right) is now in the correct color, the text is still wrong.  The area around the radio button and check box are the right color, but the text is still wrong (although note that some of the background of the static control, and the radio button and check box, has the right color, but the text is still wrong.  And the background for the edit control, for the two edit controls in the combo boxes, for the list box and for the tree control are all wrong.  Note that in some controls, part of the control (surrounding the text) is the correct color for the normal window background, white or dialog-box-color, but the rest of the control is the chosen MYCOLOR background.  While this is an improvement for the static controls, it is definitely not an improvement for the other controls.

The Good News

  • The dialog background is the correct color
  • CStatic backgrounds are the correct color
  • The background around the check box is correct
  • The background around the radio button is correct

The Bad News

  • CStatic text still has the wrong color
  • The CTreeCtrl control background is now the dialog color
  • The CListBox bckground is now the dialog color
  • The CComboBox colors are wrong for the Simple and Dropdown forms
  • The CEdit backgroup is now the dialog color
  • The edit control text of the combo boxes how shows up with the correct background, inconsistent with the rest of the control
  • The text in the CEdit control has the wrong background, inconsistent with the rest of the control
  • The text of the check box is still in the wrong color
  • The text of the radio button is in the wrong color
  • ...and that's not all!  (Keep reading)
It is now obvious that it is the text itself that has the wrong color, so the correct solution would be to SetBkColor to solve this problem.  The code would be
HBRUSH CfvcolorView::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
    {
     HBRUSH hbr = CFormView::OnCtlColor(pDC, pWnd, nCtlColor);
 
     pDC->SetBkColor(MYCOLOR);

     if((HBRUSH)brush == NULL)
        brush.CreateSolidBrush(MYCOLOR);
     return (HBRUSH) brush;
    } 

The Good News

  • The CStatic controls look good
  • The Radio Button looks good
  • The Check Box looks good
  • There is no contrasting background in the text boxes of the combo boxes
  • There is no contrasting background in the edit control

The Bad News

  • The CTreeCtrl is still wrong
  • The ListBox, although a consistent color, is wrong
  • The Combo boxes, although the background is now consistent, are wrong
  • The background of the DropList combo box is now wrong
  • The Edit control background is wrong, although the background is a consistent color
  • The CTreeCtrl control background is still the dialog color
  • The CListBox bckground is still the dialog color
  • ...and that's not all! (Keep reading)
Let's make edit controls a special exception.
HBRUSH CfvcolorView::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
    {
     HBRUSH hbr = CFormView::OnCtlColor(pDC, pWnd, nCtlColor);

     if(c_EnableCtlColor.GetCheck() != BST_CHECKED)
        return hbr;
     
     TCHAR classname[MAX_PATH];
     if(::GetClassName(pWnd->m_hWnd, classname, MAX_PATH) == 0)
        return hbr;
     if(_tcsicmp(classname, _T("EDIT")) == 0)
        return hbr;

     pDC->SetBkColor(MYCOLOR);

     if((HBRUSH)brush == NULL)
        brush.CreateSolidBrush(MYCOLOR);
     return (HBRUSH) brush;
    }
 

The Good News

  • The CStatic controls look good
  • The Radio Button looks good
  • The Check Box looks good
  • There is no contrasting background in the text boxes of the combo boxes
  • There is no contrasting background in the edit control

The Bad News

  • The CTreeCtrl control background is still the dialog color
  • The CListBox bckground is still the dialog color
  • The ListBox, although a consistent color, is wrong
  • The Combo boxes, although the background is now consistent, are wrong
  • The Edit control background is wrong, although the background is a consistent color
  • ...and that's not all! (Keep reading)

But we haven't shown one other problem.  If we look at the dropdown lists themselves, we find that the dropdown list backgrounds are still wrong.

  

but at least they are consistent with the simple dropdown presentation as well.  But what is the type of this dropdown control?  Well, a TRACE statement added after the ::GetClassName reveals that the type is COMBOLBOX.  So we can add that as another case.

HBRUSH CfvcolorView::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
    {
     HBRUSH hbr = CFormView::OnCtlColor(pDC, pWnd, nCtlColor);

     TCHAR classname[MAX_PATH];
     if(::GetClassName(pWnd->m_hWnd, classname, MAX_PATH) == 0)
        return hbr;
     if(_tcsicmp(classname, _T("EDIT")) == 0)
        return hbr;
     if(_tcsicmp(classname, _T("COMBOLBOX")) == 0)
        return hbr;
 
     pDC->SetBkColor(MYCOLOR);

     if((HBRUSH)brush == NULL)
        brush.CreateSolidBrush(MYCOLOR);
     return (HBRUSH) brush;
    }

and indeed we have made progress.  The Combo part of the Simple ComboBox on the left is now the right color, and as we see from these examples, the dropdown part of the DropDown and DropList controls is also the correct color.

 

The Good News

  • The CStatic controls look good
  • The Radio Button looks good
  • The Check Box looks good
  • There is no contrasting background in the text boxes of the combo boxes
  • There is no contrasting background in the edit control
  • The edit controls of the combo boxes are now correct
  • The dropdowns of the combo boxes are now correct
  • The CEdit control is now correct

The Bad News

  • The CTreeCtrl is still wrong
  • The ListBox, although a consistent color, is wrong
  • The static component of the DropList combo box is still wrong
That's progress, but the ComboBox part of the DropList is still wrong.  But this can be solved by excluding COMBOBOX from the background override.
HBRUSH CfvcolorView::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
    {
     HBRUSH hbr = CFormView::OnCtlColor(pDC, pWnd, nCtlColor);
     
     TCHAR classname[MAX_PATH];
     if(::GetClassName(pWnd->m_hWnd, classname, MAX_PATH) == 0)
        return hbr;
     if(_tcsicmp(classname, _T("EDIT")) == 0)
        return hbr;
     if(_tcsicmp(classname, _T("COMBOBOX")) == 0)
        return hbr;
     if(_tcsicmp(classname, _T("COMBOLBOX")) == 0)
        return hbr;

     pDC->SetBkColor(MYCOLOR);

     if((HBRUSH)brush == NULL)
        brush.CreateSolidBrush(MYCOLOR);
     return (HBRUSH) brush;
    } 

The Good News

  • The CStatic controls look good
  • The Radio Button looks good
  • The Check Box looks good
  • There is no contrasting background in the text boxes of the combo boxes
  • The static control of the DropList control is now the correct color
  • There is no contrasting background in the edit control
  • The edit controls of the combo boxes are now correct
  • The dropdowns of the combo boxes are now correct
  • The CEdit control is now correct

The Bad News

  • The CTreeCtrl is still wrong
  • The ListBox, although a consistent color, is wrong
We can now tackle those last two elements.  Rather than keep the suspense up, we'll just add LISTBOX and...

What's the name of a tree control?  Well, we could use Spy++ to look it up, and type it in, but there is actually a symbol for it.  We can look it up in commctrl.h, and find that it is WC_TREEVIEW.

So the entire OnCtlColor handler is shown below.

HBRUSH CfvcolorView::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
    {
     HBRUSH hbr = CFormView::OnCtlColor(pDC, pWnd, nCtlColor);

     TCHAR classname[MAX_PATH];
     if(::GetClassName(pWnd->m_hWnd, classname, MAX_PATH) == 0)
        return hbr;
     if(_tcsicmp(classname, _T("EDIT")) == 0)
        return hbr;
     if(_tcsicmp(classname, _T("COMBOBOX")) == 0)
        return hbr;
     if(_tcsicmp(classname, _T("COMBOLBOX")) == 0)
  .     return hbr;
     if(_tcsicmp(classname, _T("LISTBOX")) == 0)
        return hbr;
     if(_tcsicmp(classname, WC_TREEVIEW) == 0)
        return hbr;

     pDC->SetBkColor(MYCOLOR);

     if((HBRUSH)brush == NULL)
        brush.CreateSolidBrush(MYCOLOR);
     return (HBRUSH) brush;
    }

The Good News

  • The CStatic controls look good
  • The Radio Button looks good
  • The Check Box looks good
  • There is no contrasting background in the text boxes of the combo boxes
  • The static control of the DropList control is now the correct color
  • There is no contrasting background in the edit control
  • The edit controls of the combo boxes are now correct
  • The dropdowns of the combo boxes are now correct
  • The CEdit control is now correct
  • The CTreeCtrl control now has the right color
  • The CListBox control now has the right color

The Bad News

  • Bad news? What bad news?  It all works!

No Download

There is no download because you can just highlight the body of the above function, copy it, and paste it into your own handler.

Notes

I use a member variable of the class, CBrush brush; to hold the brush.  If the only code required here was to just paint the background in OnEraseBkgnd, I could have done this just with CDC::FillSolidRect which does not require that I have a brush.  But since I am going to need the brush in the OnCtlColor handler, I chose to create the brush once.

I could have created the brush in the class constructor, but I chose to illustrate how to do it dynamically.

I do want to point out that I often rail against the use of OnCtlColor handlers to handle the background of controls.  That is because if the background rules apply to one control, or even a small set of controls, then it is inappropriate to handle this in the parent, because the parent rarely, if ever, should know what color a control needs to paint itself.  The approach of checking for control IDs in the OnCtlColor handler and taking different actions based on the control IDs is very poor modular programming.  However, in this case, the rules are being applied to all controls (or all instances of a class of controls, without regard to their specific IDs), and is thus more robust because it does not do different things for individual selected controls based on their control IDs.  Note that it does not look at control IDs at all.

 

[Dividing Line Image]

The views expressed in these essays are those of the author, and in no way represent, nor are they endorsed by, Microsoft.

Send mail to newcomer@flounder.com with questions or comments about this web site.
Copyright 2007 FlounderCraft Ltd./The Joseph M. Newcomer Co. All Rights Reserved
Last modified: May 14, 2011