2
votes

There is a call back function in my main code which included by Switch case statements. After each case I defined a SetWindowText function to print a text in a static Control which created on a dialog (or parent window),something like this:

::SetWindowText(GetDlgItem(IDC_STATIC)->m_hWnd, "loading");

I am going to set background of static control as the background of dialog. Everything goes well except the text of all the cases which place on each other and I receive a static control with overlapped texts, somthing like this:

enter image description here

I don't know why in each step it does not close the static window to avoid such kind of problems. I added OnEraseBkgnd, OnDestroy, OnCtlColor messages as follow:

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

    CDC dcMemory;
    dcMemory.CreateCompatibleDC(pDC);
    CBitmap* pOldbitmap = dcMemory.SelectObject(&CBmp);
    CRect rcClient;
    GetClientRect(&rcClient);
    const CSize& sbitmap = bitmapSize;
    pDC->BitBlt(0, 0, sbitmap.cx, sbitmap.cy, &dcMemory, 0, 0, SRCCOPY);
    dcMemory.SelectObject(pOldbitmap);
    return TRUE;

}
    HBRUSH CmainDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
    HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);



    if (pWnd->GetDlgCtrlID() == IDC_STATIC)
        //Example of changing Text colour specific to a certain 
        //Static Text Contol in this case IDC_STATIC.
    {
        pWnd->GetExStyle() & (WS_EX_TRANSPARENT);
        pDC->SetBkMode(TRANSPARENT);
        pDC->SetTextColor(RGB(255, 255, 255));

    }
    if (pWnd->GetDlgCtrlID() == IDC_OPERATION)

    {
        pWnd->GetExStyle() & (WS_EX_TRANSPARENT);
        pDC->SetBkMode(TRANSPARENT);
        pDC->SetTextColor(RGB(255, 255, 0));

        // Return handle to our CBrush object
    }
        return reinterpret_cast<HBRUSH>(GetStockObject(NULL_BRUSH));
    }

void CmainDlg::OnDestroy()
{
    CDialog::OnDestroy();

    // TODO: Add your message handler code here
    Background.DeleteObject(); // Delete Background bitmap
    BrushHol.DeleteObject();
}
//subclass the static control, just to make sure the code is the only one handling WM_ERASEBKGND and WM_PAINT messages.
void CmainDlg::PreSubclassWindow()
{
    CWnd::PreSubclassWindow();

    const LONG_PTR exStyle = GetWindowLongPtr(m_hWnd, GWL_EXSTYLE);
    SetWindowLongPtr(m_hWnd, GWL_EXSTYLE, exStyle | WS_EX_TRANSPARENT);
}

Update:

I commented OnEraseBkgnd, OnDestroy, OnCtlColor functions. So I received the same overlapped texts and now with a bit more certainty I can say the problem comes from setWindowText because after finishing each Case the text remains on static control window which I defined in each of switch case statments. I tried to use the following commands but nothing happened:

EnableWindow( GetDlgItem(m_hWnd, IDC_STATIC), FALSE);
m_static.EnableWindow(FALSE);

or

::SetDlgItemText(m_hWnd, IDC_STATIC, "");

I'll appreciate any help.

2
You set the background to be transparent, is this the right behaviour ? If you render the same text on the same area in the main loop that means that the text wont be replaced, instead it will stay there and it will overlap... Try to change the background to non transparent. Do you use the same coordinates while printing the text ?KostasRim
Turns out that you don't actually like SetBkMode(TRANSPARENT)Hans Passant
@KostasRim without SetBkMode(TRANSPARENT) function, the static control loses its parent background!Braiano
@HansPassant Without SetBkMode(TRANSPARENT) it comes back again to a non transparent mode. I mean the parent background does not set as static background!Braiano
Look when you set something to be transparent each iteration of a loop that renders text on the same location will overlap. That's the whole point of a transparent object. Do you have any other options except transparent? Now if you dont really care about performance then why don't you try to set the background again in each loop iteration ? Can you try this? If you do this it will clear your entire screen and it will be ready to render text again.KostasRim

2 Answers

4
votes

Drawing opaque:

Initialize m_Brush with CreateSolidBrush and use SetBkMode(OPAQUE) instead of TRANSPARENT

Drawing transparent:

Follow Adrian McCarthy's answer and return (HBRUSH)GetStockObject(NULL_BRUSH) for static controls instead of returning m_Brush.

Make sure dialog doesn't have WS_CLIPCHILDREN flag, otherwise you run in to the same overlap problem when you re-write the text in static control.

That should do it.

Another option, this code will blend other controls with background image (edit controls, radio buttons and check boxes) Dialog can have WS_CLIPCHILDREN flag, but static control will need a positive id if it is to be redrawn (IDC_STATIC is usually set to -1). Dialog items will need WS_EX_TRANSPARENT flag as well. I didn't test this much.

Don't forget to add ON_WM_DESTROY to message map.

class TDlg : public CDialogEx
{
public:
    std::map<int, HBRUSH> BrushMap;
    CBitmap Bitmap;

    TDlg(int id, CWnd *wnd) : CDialogEx(id, wnd){};
    void MakeBrush(CDC *pdc, CDC &memdc, int id);
    BOOL OnEraseBkgnd(CDC* pDC);
    void OnDestroy();
    BOOL OnInitDialog();
    HBRUSH OnCtlColor(CDC* pDC, CWnd* wnd, UINT nCtlColor);

    DECLARE_MESSAGE_MAP()
};

BOOL TDlg::OnInitDialog()
{
    CDialogEx::OnInitDialog();
    Bitmap.LoadBitmap(IDB_BITMAP1);
    return 1;
}

void TDlg::OnDestroy()
{
    CDialogEx::OnDestroy();
    Bitmap.DeleteObject();
    for (std::map<int, HBRUSH>::iterator it = BrushMap.begin(); it != BrushMap.end(); ++it)
        if (it->second)
            DeleteObject(it->second);
}

void TDlg::MakeBrush(CDC *pdc, CDC &memdc, int id)
{
    CWnd *item = GetDlgItem(id);

    CRect rc;
    item->GetClientRect(&rc);
    item->MapWindowPoints(this, &rc);

    CBitmap bmp;
    bmp.CreateCompatibleBitmap(&memdc, rc.Width(), rc.Height());
    memdc.SelectObject(bmp);
    memdc.BitBlt(0, 0, rc.Width(), rc.Height(), pdc, rc.left, rc.top, SRCCOPY);
    BrushMap[id] = CreatePatternBrush(bmp);
}

BOOL TDlg::OnEraseBkgnd(CDC* pDC)
{
    BITMAP bm;
    Bitmap.GetBitmap(&bm);

    CDC memdc;
    memdc.CreateCompatibleDC(pDC);
    CBitmap* pOldbitmap = memdc.SelectObject(&Bitmap);
    pDC->BitBlt(0, 0, bm.bmWidth, bm.bmHeight, &memdc, 0, 0, SRCCOPY);

    //BrushMap should be intialized once
    if (!BrushMap.size())
        for (CWnd *p = GetWindow(GW_CHILD); p; p = p->GetNextWindow(GW_HWNDNEXT))
            if (p->GetDlgCtrlID() > 0)
                MakeBrush(pDC, memdc, p->GetDlgCtrlID());

    memdc.SelectObject(pOldbitmap);

    return TRUE;
}

HBRUSH TDlg::OnCtlColor(CDC* pDC, CWnd* wnd, UINT nCtlColor)
{
    HBRUSH br = CDialogEx::OnCtlColor(pDC, wnd, nCtlColor);

    pDC->SetTextColor(RGB(255, 0, 0));
    pDC->SetBkMode(TRANSPARENT);

    int id = wnd->GetDlgCtrlID();
    if (id > 0 && BrushMap[id])
        return BrushMap[id];

    if (nCtlColor == CTLCOLOR_STATIC)
        return (HBRUSH)GetStockObject(NULL_BRUSH);

    return br;
}
3
votes

The simplest solution is:

  1. Don't set the text background mode to transparent.
  2. Do set the text background color (with SetBkColor) to the color of the dialog.

The only trick is getting the color of the dialog as a COLORREF. If you're using standard stuff, you can probably just call GetSysColor with one of the stock color constants.

If the dialog background is not a solid color (e.g., a gradient fill), you'll have to do something more sophisticated:

  1. Add WS_EX_TRANSPARENT to the static control's extended window styles.
  2. Continue to use the transparent text mode. Your text will then be painted over a fresh copy of the dialog bits.

The WS_EX_TRANSPARENT style should cause the part of the dialog beneath the static control to be painted before the static control. That should "erase" the previous text, and then the static control will paint the new text.

Note that WS_EX_TRANSPARENT also makes the control transparent to hit testing (e.g., for mouse clicks). Since it's a static control, that shouldn't matter. But that's why this is not a general solution for other types of controls.