How to create an flicker free owner draw listbox using CDoubleBufferWindowImpl?
-
Hello, I am struggling to fit CListBoxCtrl and CDoubleBufferWindowImpl together. I have a owner drawn listbox ready, whose MSG_WM_DRAWITEM I handle in the parent control for now. I guess the first step would be to move the MSG_WM_DRAWITEM message handling to my custom listbox control. How would I then also fit in the double buffer technique? My solution was something like in the DoPaint method to call the original controls DefWndProc but that does not work. Any ideas?
Happy coding, Philipp Kursawe
-
Hello, I am struggling to fit CListBoxCtrl and CDoubleBufferWindowImpl together. I have a owner drawn listbox ready, whose MSG_WM_DRAWITEM I handle in the parent control for now. I guess the first step would be to move the MSG_WM_DRAWITEM message handling to my custom listbox control. How would I then also fit in the double buffer technique? My solution was something like in the DoPaint method to call the original controls DefWndProc but that does not work. Any ideas?
Happy coding, Philipp Kursawe
Hi Philipp,
Philipp Kursawe wrote:
I guess the first step would be to move the MSG_WM_DRAWITEM message handling to my custom listbox control
It may be your choice but it's not the first step. You need to use a double buffered
CListBox
, for instance:class CMyListBox : public CDoubleBufferWindowImpl<CMyListBox, CListBox, ATL::CControlWinTraits>
{
BEGIN_MSG_MAP(CMyListBox)
CHAIN_MSG_MAP(CDoubleBufferWindowImpl)
END_MSG_MAP()
void DoPaint(HDC hdc)
{
DefWindowProc(WM_PAINT, (WPARAM)hdc, 0);
}
};If you subclass the listbox in your parent dialog
OnInit()
, you can check with breakpoints that the dc referenced in yourDrawItem()
member is the MemoryDC set inCMyListBox::DoPaint()
. My test code:class CTest0View :
public CDialogImpl<CTest0View>,
public COwnerDraw<CTest0View>
{
public:
enum { IDD = IDD_TEST0_FORM };
CMyListBox m_lb;
void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
// must be implemented
return;
}
BEGIN_MSG_MAP(CTest0View)
MESSAGE_HANDLER(WM_INITDIALOG, OnInit)
CHAIN_MSG_MAP(COwnerDraw<CTest0View>)
FORWARD_NOTIFICATIONS()
END_MSG_MAP()LRESULT OnInit(UINT /\*uMsg\*/, WPARAM /\*wParam\*/, LPARAM /\*lParam\*/, BOOL& bHandled) { m\_lb.SubclassWindow(GetDlgItem(IDC\_LIST1)); m\_lb.AddString(L"Titi"); m\_lb.AddString(L"Tata"); m\_lb.AddString(L"Toto"); return bHandled = FALSE; }
};
cheers, AR
When the wise (person) points at the moon the fool looks at the finger (Chinese proverb)
-
Hi Philipp,
Philipp Kursawe wrote:
I guess the first step would be to move the MSG_WM_DRAWITEM message handling to my custom listbox control
It may be your choice but it's not the first step. You need to use a double buffered
CListBox
, for instance:class CMyListBox : public CDoubleBufferWindowImpl<CMyListBox, CListBox, ATL::CControlWinTraits>
{
BEGIN_MSG_MAP(CMyListBox)
CHAIN_MSG_MAP(CDoubleBufferWindowImpl)
END_MSG_MAP()
void DoPaint(HDC hdc)
{
DefWindowProc(WM_PAINT, (WPARAM)hdc, 0);
}
};If you subclass the listbox in your parent dialog
OnInit()
, you can check with breakpoints that the dc referenced in yourDrawItem()
member is the MemoryDC set inCMyListBox::DoPaint()
. My test code:class CTest0View :
public CDialogImpl<CTest0View>,
public COwnerDraw<CTest0View>
{
public:
enum { IDD = IDD_TEST0_FORM };
CMyListBox m_lb;
void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
// must be implemented
return;
}
BEGIN_MSG_MAP(CTest0View)
MESSAGE_HANDLER(WM_INITDIALOG, OnInit)
CHAIN_MSG_MAP(COwnerDraw<CTest0View>)
FORWARD_NOTIFICATIONS()
END_MSG_MAP()LRESULT OnInit(UINT /\*uMsg\*/, WPARAM /\*wParam\*/, LPARAM /\*lParam\*/, BOOL& bHandled) { m\_lb.SubclassWindow(GetDlgItem(IDC\_LIST1)); m\_lb.AddString(L"Titi"); m\_lb.AddString(L"Tata"); m\_lb.AddString(L"Toto"); return bHandled = FALSE; }
};
cheers, AR
When the wise (person) points at the moon the fool looks at the finger (Chinese proverb)
Thanks Alain, this works (almost)! I am using your WTL::CControlDialogImpl to show the listbox in a modal dialog. The drawing is a little off now.
void OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT item)
if (item->itemID == -1) return;WTL::CDCHandle dc(item->hDC); dc.GradientFillRect(item->rcItem, RGB(200,200,200), RGB(10,10,10), false); if (item->itemState == ODS\_FOCUS) { dc.SetTextColor(RGB(255,255,255)); } else { dc.SetTextColor(RGB(255,255,255)); } WCHAR text\[1024\]; int chars = m\_Ctrl.GetText(item->itemID, text); dc.SetBkMode(TRANSPARENT); dc.SelectFont(font); dc.DrawText(text, chars, &item->rcItem, DT\_NOPREFIX | DT\_SINGLELINE | DT\_CENTER | DT\_VCENTER | DT\_END\_ELLIPSIS);
}
After some rows the gradients stop to show up and the background is just the standard window color.
Happy coding, Philipp Kursawe