CustomDrawn listbox in a OwnerDrawn combobox
"Jean-Yves" <[email protected]>
| Newsgroups | gmane.comp.windows.wtl |
|---|---|
| Message-ID | <[email protected]> |
Hi,
I'm having a hard time understanding how to use a custom-drawn listbox in place of the native combobox listbox. You can read the whole source code below, made as short as possible (no asserts, dummy values). The first part implements a custom-drawn CListViewCtrl, with only one overriden method: OnPrePaint(). The second part implements an owner-drawn CComboBox, based on the "Tree Combo" source code from "Various ComboBox controls" article by Bjarke Viksoe (http://www.viksoe.dk/code/combos.htm). When the native combobox list is about to be displayed, OnDropDown() should prevent it from being used and display the custom-drawn list instead. And there comes my problem: the custom-drawn list method OnPrePaint() is never called so the custom-drawn listbox is never displayed. Instead, a normal lis
tbox is used by the frawework as usual. Sure I have misunderstood something, but what ?...
Any help would be greatly appreciated !
Best regards.
Jean-Yves Chasle
---------- SOURCE CODE -----------
// Custom drawn listbox control
template< class T, class TBase = CListViewCtrl, class TWinTraits = CControlWinTraits >
class ATL_NO_VTABLE MyListBoxImpl
: public CWindowImpl< T, TBase, TWinTraits >,
public CCustomDraw< T >
{
public:
typedef MyListBoxImpl< T, TBase, TWinTraits > TThisClass;
public:
DECLARE_WND_SUPERCLASS( NULL, TBase::GetWndClassName() )
// Never called when the control is part of the combobox...
DWORD OnPrePaint( int /*iIdCtrl*/, LPNMCUSTOMDRAW poNMCD ) {
return CDRF_SKIPDEFAULT;
}
private:
BEGIN_MSG_MAP( TThisClass )
CHAIN_MSG_MAP_ALT( CCustomDraw< T >, 1 )
MESSAGE_HANDLER( WM_CREATE, OnCreate )
END_MSG_MAP()
LRESULT OnCreate( UINT uiMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/ ) {
LRESULT lRes = DefWindowProc( uiMsg, wParam, lParam );
ModifyStyle( 0, (LVS_SINGLESEL | LVS_SHOWSELALWAYS) );
return lRes;
}
};
class MyListBox: public MyListBoxImpl< MyListBox > {
public:
DECLARE_WND_SUPERCLASS( _T( "MyListBox" ), GetWndClassName() )
};
// Owner drawn combobox
template< class T, class TBase = CComboBox, class TWinTraits = CControlWinTraits >
class ATL_NO_VTABLE MyComboBoxImpl: public CWindowImpl< T, TBase, TWinTraits >
{
public:
typedef MyComboBoxImpl< T, TBase, TWinTraits > TThisClass;
private:
// Works fine with CListViewCtrl, not with MyListBox (which behaves like CListViewCtrl).
CContainedWindowT< MyListBox > m_wndListbox;
public:
DECLARE_WND_SUPERCLASS( NULL, TBase::GetWndClassName() )
int InsertString( int nIndex, LPCTSTR szString ) {
return m_wndListbox.InsertItem( nIndex, szString );
}
private:
BEGIN_MSG_MAP( TThisClass )
MESSAGE_HANDLER( WM_CREATE, OnCreate )
MESSAGE_HANDLER( WM_DESTROY, OnDestroy )
MESSAGE_HANDLER( OCM_DRAWITEM, OnDrawItem )
REFLECTED_COMMAND_CODE_HANDLER( CBN_DROPDOWN, OnDropDown )
MESSAGE_HANDLER( WM_APP + 1, OnListHandleInput )
ALT_MSG_MAP( 1 )
MESSAGE_HANDLER( WM_LBUTTONDOWN, OnListButtonUp )
MESSAGE_HANDLER( WM_KILLFOCUS, OnListKillFocus )
END_MSG_MAP()
LRESULT OnCreate( UINT /*uiMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL &/*rbHandled*/ ) {
LRESULT lRes = DefWindowProc();
ModifyStyle( 0, (CBS_DROPDOWNLIST | CBS_OWNERDRAWFIXED | CBS_HASSTRINGS) );
m_wndListbox.Create( this, 1, NULL, &rcDefault, NULL, (WS_POPUP | WS_BORDER | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | LVS_SINGLESEL | LVS_SHOWSELALWAYS), 0 );
return lRes;
}
LRESULT OnDestroy( UINT /*uiMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL &rbHandled ) {
m_wndListbox.DestroyWindow();
rbHandled = FALSE;
return 0;
}
LRESULT OnDrawItem( UINT /*uiMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL &/*rbHandled*/ ) {
return TRUE;
}
LRESULT OnDropDown( WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL &rbHandled ) {
PostMessage( WM_APP + 1 ); // Calls OnListHandleInput().
rbHandled = FALSE;
return 0;
}
LRESULT OnListHandleInput( UINT /*uiMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL &/*rbHandled*/ ) {
ReleaseCapture(); // (Original comment by Bjarke Viksoe:) Force list to close prematurely!
// Set our list control rect from the native combobox list rect.
COMBOBOXINFO oCBInfo;
oCBInfo.cbSize = sizeof( oCBInfo );
GetComboBoxInfo( &oCBInfo );
RECT rcList;
::GetWindowRect( oCBInfo.hwndList, &rcList );
rcList.bottom = rcList.top + 100; //-- Dummy value
// Make our list control show in place of the native combobox list.
::ShowWindow( oCBInfo.hwndList, SW_HIDE );
m_wndListbox.SetWindowPos( HWND_TOP, &rcList, SWP_SHOWWINDOW );
m_wndListbox.SetFocus();
return 0;
}
LRESULT OnListButtonUp( UINT /*uiMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL &/*rbHandled*/ ) {
LRESULT lRes = m_wndListbox.DefWindowProc();
SetFocus(); // Calls OnListKillFocus().
return lRes;
}
LRESULT OnListKillFocus( UINT /*uiMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL &rbHandled ) {
m_wndListbox.ShowWindow( SW_HIDE );
rbHandled = FALSE;
return 0;
}
};
class MyComboBox: public MyComboBoxImpl< MyComboBox > {
public:
DECLARE_WND_SUPERCLASS( _T( "MyComboBox" ), GetWndClassName() )
};
---------- Excerpt from CMainDlg::OnInitDialog() -----------
MyComboBox m_wndCombobox.Create( m_hWnd, CRect( CPoint( 400, 0 ), CSize( 100, 20 ) ), NULL, (WS_VISIBLE | WS_CHILD | WS_TABSTOP | WS_GROUP | CBS_DROPDOWNLIST | CBS_HASSTRINGS) );
// Use m_wndCombobox.InsertString() to populate it.
------------------------------------
Yahoo! Groups Links
<*> To visit your group on the web, go to:
http://groups.yahoo.com/group/wtl/
<*> Your email settings:
Individual Email | Traditional
<*> To change settings online go to:
http://groups.yahoo.com/group/wtl/join
(Yahoo! ID required)
<*> To change settings via email:
[email protected]
[email protected]
<*> To unsubscribe from this group, send an email to:
[email protected]
<*> Your use of Yahoo! Groups is subject to:
http://docs.yahoo.com/info/terms/