0
votes

Let me begin with the goal: I'm going to set a static control's background as a dialog while the static control content is not a fixed string and after a switch case statement,changes. In other word, I'm going to set the background of a static control with a dynamic content to a dialog window with a static bimap image.

To arrive at this goal, three handle messages defined as below: OnEraseBkgnd(), OnDestroy(), OnCtlColor()

To show the texts, after each Switch case statement I set a SetWindowText function:

::SetWindowText(GetDlgItem(IDC_STATIC_FORM)->m_hWnd, ArrayName);

The only problem here was overlapping. All the texts from the previews events or cases remained at SetWindowText. To solve this problem after each SetWindowText a InvalidateRect(Null) defined and this problem also solved but during the procedure I received a dialog which was always blinking and if I say technically, flickering. I think the problem is just because of Invalidate which is applied to the whole dialog not just static control. The OnCtlColor is overriden for the dialog not the control. Since I just want the control to invalidate, then I must subclass the CStatic control, override only it's OnCtlColor (not the dialog's), and call only it's Invalidate instead.

My problem:

Assuming I have declared a variable for static control in the main code and initialised it in the constructor I defined a class named CSTATICCTRL with a base class CDialogEX then the message handlers were as follows:

Variable member defined as:

CSTATICCTRL m_STATIC_FORM;

and

BOOL CSTATICCTRL::OnEraseBkgnd(CDC* pDC)
{
    // TODO: Add your message handler code here and/or call default
    CDC dcMemory;
    dcMemory.CreateCompatibleDC(pDC);
    CBitmap* pOldbitmap = dcMemory.SelectObject(&Background);
    CRect rcClient;

    const CSize& sbitmap = bitmapSize;
    pDC->BitBlt(0, 0, sbitmap.cx, sbitmap.cy, &dcMemory, 0, 0, SRCCOPY);
    /*The BitBlt function performs a bit-block transfer of the color data corresponding to a rectangle of pixels
    from the specified source device context into a destination device context.*/
    dcMemory.SelectObject(pOldbitmap);

    return 1;
    //return CDialogEx::OnEraseBkgnd(pDC);
}

HBRUSH CSTATICCTRL::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
    //HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);

    if (pWnd->GetDlgCtrlID() == IDC_STATIC_FORM)
    {
        pDC->SetBkMode(TRANSPARENT);
        pDC->SetTextColor(RGB(255, 255, 255));
        Invalidate();

    }
    return (HBRUSH)GetStockObject(NULL_BRUSH);

}
void CSTATICCTRL::OnDestroy()  //free the resources created for the bitmap,
{
    CDialog::OnDestroy();
    Background.DeleteObject(); // Delete Background bitmap
    BrushHol.DeleteObject();
    // Delete Brush
}

I left these messages on the main code too. Then I tried to invalidate the static control after each switch case in this way:

 m_STATIC_FORM.Invalidate();

Furthermore a PreSubclassWindow() handle added at the end of new class as follows.

void CSTATICCTRL::PreSubclassWindow()
{
    // TODO: Add your specialized code here and/or call the base class

    CDialogEx::PreSubclassWindow();
    ModifyStyle(0, BS_OWNERDRAW);   // make the button owner drawn

} 

Nothing happened!I don't know what's wrong with my code!! In this new way the static control content changes but it's not transparent and overlaps still exist.

UPDATE:

Actually just now I realized that the new Class doesn't have any effect on the static control and dialog box after commenting some lines!! I think firstly I should active CSTATICCTRL then I can work on its handle messages.

For the dialog code these three handle messages are employed:

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(&Background);
    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;
    //return CDialog::OnEraseBkgnd(pDC);
}




void CMainDlg::OnDestroy()  //free the resources created for the bitmap,
{
    CDialog::OnDestroy();

    // Delete Brush
}

HBRUSH CMainDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{

    // TODO:  Change any attributes of the DC here

    return (HBRUSH)GetStockObject(NULL_BRUSH);
}
1
Do you know the Windows API enough to understand, how MFC relates to it? It sounds like you're pretty much lost, trying to learn two APIs at the same time, where one is a mandatory prerequisite for the other.IInspectable
Not yet!!!!! However I'm learning. Ok, thanks.Braiano
@IInspectable you can learn both at the same time, that's how I did it. But you need to be aware of the boundaries. And there are still things I don't have a good understanding of, like the routing of messages between parent and child windows, because MFC hides some of that from you.Mark Ransom
@Mark: As you point out, when you try to learn both at the same time, you won't become fluent in both. I'd rather not promote wasting time, for too little in return. Besides, MFC doesn't hide anything. If you need to find out about message routing, you can read the TN articles, as well as the source code, if necessary.IInspectable

1 Answers

2
votes

I haven't tried this, but I think it will work.

Override OnCtlColor in your dialog. When it detects that the static control is being drawn, return a null brush. This keeps the background from erasing, which is what causes the flicker.

Set the WS_EX_TRANSPARENT style on your static control. This will cause the part of the dialog underneath the control to be repainted before the control is drawn, erasing the old text.

Edit: According to an article by Raymond Chen the WS_EX_TRANSPARENT style won't help in this case, because the dialog isn't a sibling of the static control, it's the parent. You can make it redraw the portion underneath your static control with RedrawWindow.

You shouldn't need your own class, CStatic will work fine with these modifications. If you do decide you need your own class, you shouldn't derive it from CDialogEx though, that should be reserved for dialog windows.

pStatic->SetWindowText(str);
CRect rcChild;
pStatic->GetClientRect(&rcChild);
pStatic->MapWindowPoints(this, &rcChild);
RedrawWindow(rcChild, NULL, RDW_INVALIDATE | RDW_UPDATENOW);
pStatic->InvalidateRect(NULL);