Click here to Skip to main content
15,887,746 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I am designing a GUI that makes use of a checkbox. I need the colour of the chechbox, i.e. the area where the black tick appears, to change when the box is checked.

What I have tried:

I read about various approached online which suggest using the WM_CTLCOLOR message and using the OnCtlColor function, but this changes the colour immediately on initialisation. So what I did was to try and change the colour when the box is checked as follows:

void CMyAppDlg::OnBnClickedCheckTxpow()
{
	if (m_chkTxPow.GetState() == 9)
	{
		CDC *pDC;
		CWnd *pWnd;

		//pWnd->SetDlgCtrlID(IDC_CHECK_TXPOW);
		pDC = m_chkTxPow.GetDC();
		pDC->SetBkColor(RGB(0,255,0));
	}	
}


m_chkTxPow is a control variable assigned to the check box. I was trying to emulate the OnCtlColor functions available online as closely as possible. But this does not work.
Also I don't think changing the background colour affects the colour inside the box. Could anyone help me here?
Posted
Updated 20-Jun-17 21:49pm

1 solution

There is no simple method to change the background colour of check boxes. The reason is that MFC check boxes are system controls which do not provide options to change the background colour for the arrow area and that area is not transparent.

The only option to do this is deriving a CButton class that uses owner drawing. There you can draw an unchecked box using system functions. For checked boxes fill then the background and draw the arrow manually.

This can be done for classic check boxes using CDC::DrawFrameControl, filling the background with CDC::FillSolidRect (use InflateRect(-2, -2) with the check box size) and drawing the arrow.

But applications do not use the classic style nowadays. Then you have to use DrawThemeBackground, fill the background using CDC::GradientFill, and draw the arrow shaded too. But that will not look exactly like the system drawn check boxes.

// Draw default sized check box.
// Classic        Themed
//
// LLLLLLLLLLLLW  BBBBBBBBBBBBB
// LGGGGGGGGGGSW  B           B
// LG         SW  B           B
// LG       K SW  B        G  B
// LG      KK SW  B       GG  B
// LG K   KKK SW  B  G	 GGG  B
// LG KK KKK  SW  B  GG GGG   B
// LG KKKKK   SW  B  GGGGG    B
// LG  KKK    SW  B   GGG     B
// LG   K     SW  B    G      B
// LSSSSSSSSSSSW  B           B
// WWWWWWWWWWWWW  BBBBBBBBBBBBB
// 
// L = light gray, G = dark gray, S =very light gray, B/K = black, W = white
//
// rcBox:  Rect containing the check box
// clrBk:  Background colour
// clrArr: Colour for arrow (COLOR_BTNSHADOW if disabled; else COLOR_WINDOWTEXT)

// save bk color; is changed by drawing functions
int nBkClr = pDC->GetBkColor(); 

#if 1
// Add aditional states (inactive, pushed) as required
pDC->DrawFrameControl(rcBox, DFC_BUTTON, DFCS_BUTTONCHECK);
#else
// Themed (set nThemedState accordingly):
DrawThemeBackground(m_hTheme, 
    lpDrawItemStruct->hDC, 
    BP_CHECKBOX, nThemedState, 
    &rcBox, NULL);
#endif

CRect rcBk(rcBox);
// Use -3 when themed and CBS_UNCHECKEDHOT
rcBk.InflateRect(-2, -2);
#if 1
pDC->FillSolidRect(&rcBk, clrBk);
#else
// Themed:
// Two triangles: upper left and lower right
static GRADIENT_TRIANGLE TriG[2] = { { 0, 1, 3 }, { 0, 3, 2} };
#define GetR(rgb)   ((int)((rgb) & 0xFF)) // extract R/G/B values fro COLORREF
#define GetG(rgb)   ((int)(((rgb) >> 8) & 0xFF))
#define GetB(rgb)   ((int)(((rgb) >> 16) & 0xFF))
#define Darken(a)   ((a) - ((a) >> 1)) // make R/G/B part darker
#define Brighten(a) ((a) + ((0xFF - (a)) >> 1))	// make R/G/B part brighter
#define Col16(c)    ((COLOR16)((c) << 8)) // convert R/G/B part to COLOR16 value
#define Bright16(a) (Col16(Brighten(a)))
TRIVERTEX TriV[4] = {
{rc.left, rc.top, Col16(GetR(clr)), Col16(GetG(clr)), Col16(GetB(clr)), 0},
{rc.right, rc.top, Bright16(GetR(clr)), Bright16(GetG(clr)), Bright16(GetB(clr)), 0},
{rc.left, rc.bottom, Bright16(GetR(clr)), Bright16(GetG(clr)), Bright16(GetB(clr)), 0},
{rc.right, rc.bottom, 0xff00, 0xff00, 0xff00, 0}
};
pDC->GradientFill(TriV, 4, TriG, 2, GRADIENT_FILL_TRIANGLE);
#endif

// Themed: clrArr = RGB(76, 97, 152); // not shaded!
CRect rcArr(rcBox.left + 3, // left start position
    rcBox.top + 5, // top start position
    rcBox.left + 4, // width 1 pixel
    rcBox.top + 8); // height 3 pixels
for (int i = 0; i < 7; i++) 
{
    pDC->FillSolidRect(&rcArr, clrArr);
    rcArr.OffsetRect(1, (i < 2) ? 1 : -1);
}

pDC->SetBkColor(nBkClr); // restore bk color
 
Share this answer
 
Comments
TheLostJedi 21-Jun-17 7:06am    
Thank you!! This strategy works!! I never thought I'd have to spend so much time on a checkbox. Any idea why Microsoft does not allow finer control of its controls like (for example) Qt?
Jochen Arndt 21-Jun-17 7:30am    
Thank you for your feedback and accepting my solution.

The classic Windows controls has been created a long time ago and current Windows versions require to be compatible with the old interfaces.

They could have made it better with the themed versions, but only MS would know why they have not done so. There might be a better solution but I don't know of one (there is not so much documentation about themed controls).
TheLostJedi 21-Jun-17 7:50am    
Given the disadvantageous position we are in, this is the best possible solution. So thank you!

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900