Thursday, July 24, 2008

CStatic control loses transparency in Visual C++

I want to display some information on a control with transparent background. I am using a CStatic control and when opening, the control has a transparent background. But when I try to set the desired information in the control, my control loses transparency and it becomes like a basic CStatic control although it has been set to be transparent. I have overridden the OnPaint method from the derived CStatic class named StaticCtrl. Here is the code of the OnPaint method. I don't do anything else. I only override OnPaint.

void StaticCtrl::OnPaint()
{
    CPaintDC dc(this); // device context for painting
   
    // Where to draw text
    CRect clientRect;
    GetClientRect(clientRect);
   
    // Get the caption
    CString strTitle;
    GetWindowText(strTitle);
   
    // Get the font
    CFont *pFont, *pOldFont;
    pFont = GetFont();
    pOldFont = dc.SelectObject(pFont);
   
    DWORD dwStyle = GetStyle(), dwText = 0;
   
    // Map "Static Styles" to "Text Styles"
    #define MAP_STYLE(src, dest)
        if(dwStyle & (src)) dwText |= (dest)
    #define NMAP_STYLE(src, dest)
        if(!(dwStyle & (src))) dwText |= (dest)
   
    MAP_STYLE(SS_RIGHT, DT_RIGHT);
    MAP_STYLE(SS_CENTER, DT_CENTER);
    MAP_STYLE(SS_CENTERIMAGE, DT_VCENTER | DT_SINGLELINE);
    MAP_STYLE(SS_NOPREFIX, DT_NOPREFIX);
    MAP_STYLE(SS_WORDELLIPSIS, DT_WORD_ELLIPSIS);
    MAP_STYLE(SS_ENDELLIPSIS, DT_END_ELLIPSIS);
    MAP_STYLE(SS_PATHELLIPSIS, DT_PATH_ELLIPSIS);
    NMAP_STYLE(SS_LEFTNOWORDWRAP | SS_CENTERIMAGE |
               SS_WORDELLIPSIS | SS_ENDELLIPSIS |
               SS_PATHELLIPSIS, DT_WORDBREAK );
   
    // Set transparent background
    dc.SetBkMode(TRANSPARENT);
   
    // Draw the text
    if(GetDlgCtrlID() == IDC_STATIC_CANCEL)
    dc.SetTextColor(RGB(255, 0, 0));
   
    if(GetDlgCtrlID() == IDC_CONNECTIONTXT ||
       GetDlgCtrlID() == IDC_RATETXT)
    dc.SetTextColor(RGB(250, 100, 0));
   
    if(GetDlgCtrlID() == IDC_PERCENT ||
       GetDlgCtrlID() == IDC_REMAINING ||
       GetDlgCtrlID() == IDC_TIMETXT)
       dc.SetTextColor(RGB(250, 100, 0));

    dc.DrawText(strTitle, clientRect, dwText);
   
    // Select old font
    dc.SelectObject(pOldFont);
}

Solution: If you have changed the CDialog background color, you will notice that CStatic & some other controls' background colors still show as the system default COLOR_BTNFACE color. To handle this issue, just make the controls' background transparent so it takes up the color of the dialog's background. Use something like this in your subclassed CDialog's OnCtlColor() event handler to make the background of all static text controls on the dialog transparent. This code considers CMyDialog was derived from CDialog.:

HBRUSH CMyDialog::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
    HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
    CBrush m_brHollow = m_brHollow.CreateStockObject(HOLLOW_BRUSH);

    if (nCtlColor == CTLCOLOR_EDIT) {
        pDC->SetTextColor(RGB(0, 0, 0));  // text color
        pDC->SetBkMode(TRANSPARENT);      // background mode
        hbr = m_brHollow;                 // return NULL brush
    }
   
    return hbr;
}

If you have painted your dialog background with WHITE, CMyDialog::OnCtlColor() I posted above works fine with CStatic controls on the dialog. But if you have a CSliderCtrl control on the dialog, you'll see that the code above makes the background color of the CSliderCtrl control pitch BLACK.

A work-around is simply to return a handle to a WHITE brush instead of a NULL brush like the code below. This code snippet considers CMyDialog was derived from CDialog & the dialog background was painted white by overriding the CDialog::OnEraseBkgnd() method.

HBRUSH CMyDialog::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
    HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
    CBrush m_brBgColor;

    // dialog background WHITE
    m_brBgColor.CreateSolidBrush(RGB(255, 255, 255));
   
    if (nCtlColor == CTLCOLOR_EDIT) {
        pDC->SetTextColor(RGB(0, 0, 0)); // text color
        pDC->SetBkMode(TRANSPARENT);     // background mode
        hbr = m_brBgColor;               // return WHITE brush
    }
   
    return hbr;
}

Refer this.