Coloring CHeaderCtrl

Flaviu_ 971 Reputation points
2021-10-08T11:59:16.893+00:00

I struggle to color text and background of a CHeaderCtrl, named CHeaderCtrlEx.

I sublclassed this control, CHeaderCtrl to a CListView, and I overridden OnNMCustomDraw for that (and OnEraseBkgnd):

BOOL CHeaderCtrlEx::OnEraseBkgnd(CDC* pDC)  
{  
 // TODO: Add your message handler code here and/or call default  

 CRect rc;  
 GetClientRect(&rc);  
 pDC->SetBkMode(TRANSPARENT);  
 pDC->FillSolidRect(&rc, RGB(255, 255, 0));  
 return TRUE;  

 return CHeaderCtrl::OnEraseBkgnd(pDC);  
}  

void CHeaderCtrlEx::OnNMCustomdraw(NMHDR* pNMHDR, LRESULT* pResult)  
{  
 LPNMLVCUSTOMDRAW pNMCD = reinterpret_cast<LPNMLVCUSTOMDRAW>(pNMHDR);  
 // TODO: Add your control notification handler code here  

 switch (pNMCD->nmcd.dwDrawStage)  
 {  
 case CDDS_PREPAINT:  
 *pResult = CDRF_NOTIFYITEMDRAW;  
 break;  
 case CDDS_ITEMPREPAINT:  
 CDC* pDC = CDC::FromHandle(pNMCD->nmcd.hdc);  
 pDC->SetBkMode(TRANSPARENT);  
 pDC->SetBkColor(RGB(280, 180, 200));  
 pDC->SetTextColor(RGB(30, 40, 200));  
 break;  
 }  
}  

and everything pretty good (the color values doesn't matter):

![138867-image.png]1

So, what's the problem ? The problem become when I apply the windows theme in framework.h:

#ifdef _UNICODE  
#if defined _M_IX86  
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"")  
#elif defined _M_X64  
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"")  
#else  
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")  
#endif  
#endif  

After I include this code, my header looks like that:

138876-image.png

I mean, the text preserved the color, but not the background. How can I overcome this ? I attached here a sample project: https://1drv.ms/u/s!AtSPCxnvttzehHtyWx2stSJitqjB?e=gH6fef

C++
C++
A high-level, general-purpose programming language, created as an extension of the C programming language, that has object-oriented, generic, and functional features in addition to facilities for low-level memory manipulation.
3,626 questions
0 comments No comments
{count} votes

Accepted answer
  1. Castorix31 83,101 Reputation points
    2021-10-08T14:22:46.69+00:00

    It is because the Header draws the text with Theme APIs when a theme is set
    You can either remove the theme (with SetWindowTheme) or draw in CDDS_POSTPAINT
    I did a quick test in C++/Win32 by drawing the background in yellow in CDDS_POSTPAINT for the first Header item (test with WM_PRINTCLIENT, FillRect in yellow, TransparentBlt to draw white pixels in yellow)

    138906-header-backgroundcolor-postpaint.gif

    2 people found this answer helpful.

4 additional answers

Sort by: Most helpful
  1. Flaviu_ 971 Reputation points
    2021-10-29T10:34:33.847+00:00

    I realized that I need also the pressed state, so, I changed the code accordingly as follow:

                if (hTheme)
                {
                    HDC hDCTest = CreateCompatibleDC(hDC);
                    HBITMAP hBitmapTest = CreateCompatibleBitmap(hDC, nWidth, nHeight);
                    HBITMAP hBitmapTestOld = (HBITMAP)SelectObject(hDCTest, hBitmapTest);
                    RECT rectTest = { 0, 0, rect.right - rect.left, nHeight };
                    DrawThemeBackground(hTheme, hDCTest, HP_HEADERITEM, HIS_NORMAL, &rectTest, NULL);
                    clrBackground = GetPixel(hDCTest, 1, 1);
                    DrawThemeBackground(hTheme, hDCTest, HP_HEADERITEM, HIS_HOT, &rectTest, NULL);
                    clrBackgroundHot = GetPixel(hDCTest, 1, 1);
                    DrawThemeBackground(hTheme, hDCTest, HP_HEADERITEM, HIS_PRESSED, &rectTest, NULL);
                    clrBackgroundPress = GetPixel(hDCTest, 1, 1);
                    SelectObject(hDCTest, hBitmapTestOld);
                    DeleteObject(hBitmapTest);
                    DeleteDC(hDCTest);
                }
    

    and

                HDC hDCTemp = CreateCompatibleDC(hDC);
                HBITMAP hBitmapTemp = CreateCompatibleBitmap(hDC, nWidth, nHeight);
                HBITMAP hBitmapTempOld = (HBITMAP)SelectObject(hDCTemp, hBitmapTemp);
                // White => Yellow
                TransparentBlt(hDC, 0, 0, nWidth, nHeight, hDCMem, 0, 0, nWidth, nHeight, clrBackground);
                BitBlt(hDCTemp, 0, 0, nWidth, nHeight, hDC, 0, 0, SRCCOPY);
                // Green brush
                HBRUSH hBrushHot = CreateSolidBrush(RGB(0, 255, 0));
                HBRUSH hBrushHotOld = (HBRUSH)SelectObject(hDC, hBrushHot);
                FillRect(hDC, &rect, hBrushHot);
                SelectObject(hDC, hBrushHotOld);
                DeleteObject(hBrushHot);
                // Light Blue => Green
                TransparentBlt(hDC, 0, 0, nWidth, nHeight, hDCTemp, 0, 0, nWidth, nHeight, clrBackgroundHot);
                // Red brush
                HBRUSH hBrushPressed = CreateSolidBrush(RGB(255, 0, 0));
                HBRUSH hBrushPressedOld = (HBRUSH)SelectObject(hDC, hBrushPressed);
                FillRect(hDC, &rect, hBrushPressed);
                SelectObject(hDC, hBrushPressedOld);
                DeleteObject(hBrushPressed);
                // Green => Red
                TransparentBlt(hDC, 0, 0, nWidth, nHeight, hDCTemp, 0, 0, nWidth, nHeight, clrBackgroundPress);
    
                SelectObject(hDCTemp, hBitmapTempOld);
                DeleteObject(hBitmapTemp);
                DeleteDC(hDCTemp);
    

    but now the hot state is not working.

    I have tried to use a second hTempDC, but didn't work either. Is there possible to achive the pressed state beside hot state ? If it is, how ?

    1 person found this answer helpful.

  2. Castorix31 83,101 Reputation points
    2021-10-29T22:46:31.163+00:00

    [Cannot post in comments with the (stupid) 1000 characters limit...]

    In your last code you call twice TransparentBlt into hDC without saving it, so you erase it...

    Test with the 3 colors (replaced by Yellow, Green, Blue) =>
    (CreateDIBSection could be used to replace 3 colors at same time, instead of the successive TransparentBlt)

    145135-listviewheadercolors2.gif

    						HDC hDCTemp = CreateCompatibleDC(hDC);  
    						HBITMAP hBitmapTemp = CreateCompatibleBitmap(hDC, nWidth, nHeight);  
    						HBITMAP hBitmapTempOld = (HBITMAP)SelectObject(hDCTemp, hBitmapTemp);  
      
    						HDC hDCTemp2 = CreateCompatibleDC(hDC);  
    						HBITMAP hBitmapTemp2 = CreateCompatibleBitmap(hDC, nWidth, nHeight);  
    						HBITMAP hBitmapTempOld2 = (HBITMAP)SelectObject(hDCTemp2, hBitmapTemp2);  
      
    						// Normal color (White) => Yellow  
    						TransparentBlt(hDC, 0, 0, nWidth, nHeight, hDCMem, 0, 0, nWidth, nHeight, clrBackground);  
      
    						// Copy hDC to 2 temporary DC  
    						BitBlt(hDCTemp, 0, 0, nWidth, nHeight, hDC, 0, 0, SRCCOPY);						  
    						BitBlt(hDCTemp2, 0, 0, nWidth, nHeight, hDC, 0, 0, SRCCOPY);  
      
    						// Green brush in temp DC 2  
    						HBRUSH hBrushHot = CreateSolidBrush(RGB(0, 255, 0));  
    						HBRUSH hBrushHotOld = (HBRUSH)SelectObject(hDCTemp2, hBrushHot);  
    						FillRect(hDCTemp2, &rectFill, hBrushHot);  
    						SelectObject(hDCTemp2, hBrushHotOld);  
    						DeleteObject(hBrushHot);  
      
    						// Hot color (Light Blue) => Green  
    						TransparentBlt(hDCTemp2, 0, 0, nWidth, nHeight, hDCTemp, 0, 0, nWidth, nHeight, clrBackgroundHot);  
      
    						// Blue brush in hDC  
    						HBRUSH hBrushPressed = CreateSolidBrush(RGB(0, 0, 255));  
    						HBRUSH hBrushPressedOld = (HBRUSH)SelectObject(hDC, hBrushPressed);  
    						FillRect(hDC, &rectFill, hBrushPressed);  
    						SelectObject(hDC, hBrushPressedOld);  
    						DeleteObject(hBrushPressed);  
    						  
    						// Pressed color => Blue  
    						TransparentBlt(hDC, 0, 0, nWidth, nHeight, hDCTemp2, 0, 0, nWidth, nHeight, clrBackgroundPressed);  
    						SelectObject(hDCTemp, hBitmapTempOld);  
    						DeleteObject(hBitmapTemp);  
    						DeleteDC(hDCTemp);  
      
    						SelectObject(hDCTemp2, hBitmapTempOld2);  
    						DeleteObject(hBitmapTemp2);  
    						DeleteDC(hDCTemp2);  
    
    1 person found this answer helpful.

  3. Flaviu_ 971 Reputation points
    2021-10-10T16:58:24.897+00:00

    Interesting approach. Could you post the code with this drawing ? Actually, I need to turn to black all white pixels, but most important, not text, and NOT sorting arrow !

    I mean, what is the simplest mode to use TransparentBlt without any brush or bitmap.

    Thank you.


  4. Flaviu_ 971 Reputation points
    2021-10-31T07:45:54.123+00:00

    Interesting. I did the same trial, except this line:

    BitBlt(hDCTemp2, 0, 0, nWidth, nHeight, hDC, 0, 0, SRCCOPY);
    

    I'll return with feedback.

    0 comments No comments