How to draw the backround of a CDockablePane at startup

53 Views Asked by At

I have a dockable pane derived from CDockablePane (attached to a frame with a view), which contains two edit controls spaced apart. I expect the space between these controls to be painted on the default background color of that pane. Instead, when my app. starts the background is transparent. When I (auto-)hide/show the pane, that background is transparent - shows whatever is in the view behind it.

I did the following:

  1. Overridden OnSize() and called DoPaint() (see the code below). With this, the background repaints fine only when I resize or dock the pane. At startup, OnSize() gets called six times before the pane & view show up on screen, so it is of no help.

  2. Added OnEraseBkgnd() and and called DoPaint() (see below). It looks like OnEraseBkgnd() is not called at all at startup. This fix was suggested by an old codeguru post.

  3. Overridden OnShowWindow() and called DoPaint(). At startup, OnShowWindow() gets called five times before the pane shows up and it is of no help.

Where else should I call DoPaint()? (I don't know well which methods are called at startup. Is there a net resource or a book where I can read about that topic?)

Thank you.

EDIT:

Below is part of the code - it creates two edit controls spaced apart by cmdDelta.y. I do not paint or erase anything at this moment, just re-locate the controls in OnSize().

class CSerialPortEdit : public CRichEditCtrl
{ .... };


class COutputWnd : public CDockablePane
{
public:
    COutputWnd() { };

public:
    CSerialPortEdit m_wndPortEdit;
    CMFCEditBrowseCtrl m_cmdLine;
    CFont m_cmdFont;

public:
    virtual ~COutputWnd();
    void UpdateFonts();

protected:
    afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
    afx_msg void OnSize(UINT nType, int cx, int cy);
    afx_msg void OnSetFocus(CWnd* pOldWnd);
    afx_msg BOOL OnEraseBkgnd(CDC* pDC);

    DECLARE_MESSAGE_MAP()
};


int COutputWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if (CDockablePane::OnCreate(lpCreateStruct) == -1)
        return -1;

    // Create port edit:
    const DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_BORDER | ES_LEFT | ES_AUTOVSCROLL | ES_MULTILINE | ES_WANTRETURN;
    if (!m_wndPortEdit.Create(dwStyle, CRect(0,0,0,0), this, IDC_SERIAL_EDIT))
    {
        TRACE0("Failed to create port window\n");
        return -1;      // fail to create
    }

    // Create command line:
    const DWORD edStyle = WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT | WS_TABSTOP | ES_AUTOHSCROLL | ES_MULTILINE | ES_WANTRETURN;
    if (!m_cmdLine.CreateEx(WS_EX_CLIENTEDGE, _T("EDIT"), NULL, edStyle, CRect(0,0,0,0), this, IDC_COMMAND_EDIT))
    {
        TRACE0("Failed to create command line\n");
        return -1;      // fail to create
    }
    m_cmdLine.SetMargins(4, 4);
    m_cmdLine.EnableFileBrowseButton();
    UpdateFonts();
    return 0;
}


void COutputWnd::OnSize(UINT nType, int cx, int cy)
{
    CDockablePane::OnSize(nType, cx, cy);
    CDC* pdc = GetDC();
    DoPaint(pdc);   // redraw background
    ReleaseDC(pdc);

    m_wndPortEdit.SetWindowPos (NULL, 0, 0, cx, cy-cmdSize.y-cmdDelta.y-2, SWP_NOACTIVATE | SWP_NOZORDER);
    m_cmdLine.SetWindowPos (NULL, 0, cy-cmdSize.y-2, cx, cmdSize.y, SWP_NOACTIVATE | SWP_NOZORDER);
}


void COutputWnd::OnSetFocus(CWnd* pOldWnd)
{
    CDockablePane::OnSetFocus(pOldWnd);
    m_wndPortEdit.SetFocus();
}


BOOL COutputWnd::OnEraseBkgnd(CDC* pDC)
{
    CDC* pdc = GetDC();
    DoPaint(pdc);   // redraw background
    ReleaseDC(pdc);
    return CWnd::OnEraseBkgnd(pDC);
}
1

There are 1 best solutions below

1
Constantine Georgiou On BEST ANSWER

I tested the COutputWnd window of a test-project generated by VS. Removed the list-windows and the tab control. The problem became apparent immediately (without having to add your two controls), ie it was transparent, not erasing its background. Checked the window with the SpyXX utility and found that its class has a background brush set to COLOR_3DFACE (the standard color of dialog-box background).

So why doesn't it work? Checked the MFC headers and sources, and found the culprit: COutputWnd derives from CDockablePane <- CPane <- CBasePane. CBasePane has an OnEraseBkgnd() override, which you can find in file afxbasepane.cpp:

BOOL CBasePane::OnEraseBkgnd(CDC* /*pDC*/)
{
    return TRUE;
}

That is, it doesn't do anything and returns TRUE, indicating that no further erasing is required. This is probably because all those "pane" windows are supposed to have their client area fully covered by another control (tab, tree-view etc), so no erasing the background is "needed". But here you have removed the original tab-control and the (disabled) background was unveiled. So, there is a number of solutions, all involving overriding OnEraseBkgnd(), and your choice is a matter of preference:

A. Use the Visual Manager background color (Preferred)

BOOL COutputWnd::OnEraseBkgnd(CDC* pDC)
{
    DoPaint(pDC); // Fill background with the standard theme color
    return TRUE;  // No further erasing is required
}

DoPaint() is a method of CBasePane, and its implementation is in afxbasepane.cpp as well, immediately below OnEraseBkgnd(); calls CMFCVisualManager::OnFillBarBackground().

B. Use the Default (WNDCLASS) background color

BOOL COutputWnd::OnEraseBkgnd(CDC* pDC)
{
    return CWnd::OnEraseBkgnd(pDC); // Erase background using the class background brush.
}

This implementation "restores" the original OnEraseBkgnd() behavior, to circumvent the override in CBasePane. The original implementation (in CWnd) fills the background with the class background brush. It is set to COLOR_3DFACE, which is the standard Windows color of dialog-box backgrounds.

C. Use a Custom background color

HBRUSH hBrYellow = CreateSolidBrush(RGB(255, 255, 0)); // Yellow Brush

BOOL COutputWnd::OnEraseBkgnd(CDC* pDC)
{
    RECT rct;
    GetClientRect(&rct);
    FillRect(pDC->m_hDC, &rct, hBrYellow); // Fill the client area with the yellow brush
    return TRUE; // No further erasing is required
}

You should also remove ALL other GetDC()/DoPaint()/ReleaseDC() calls (eg in OnSize() or elsewhere). Put a Beep(1000,50); call in your OnEraseBkgnd() implementation, to get an audible indication of when and how often it is called. It should be called when the window is being enlarged (so its newly uncovered background must be erased), but not when it's being shrunk - I don't know the effect of repositioning of the two controls though. I would rather move the CDockablePane::OnSize(nType, cx, cy); call in COutputWnd::OnSize() after the code repositioning the controls.