Click here to Skip to main content
15,895,801 members
Articles / Desktop Programming / MFC

An MFC-CListCtrl derived class that allows other ‘controls’ to be inserted into a particular cell

Rate me:
Please Sign up or sign in to vote.
4.92/5 (54 votes)
5 Jan 2014CPOL12 min read 168.4K   14K   160  
A class derived from CListCtrl that allows edit controls, combo boxes, check boxes, date pickers, and color pickers to be inserted into or removed from particular cells extremely easily. The inserted 'controls' are not CWnd-derived.
//
// popupscrolllist.cpp : implementation file
//

#include "stdafx.h"
#include "..\ConfigListCtrl.h"
#include "popupscrolllist.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CPopUpScrollList

CPopUpScrollList::CPopUpScrollList() : m_lFirstPos(0), m_lSelectedItem(0), m_bActivated(FALSE) 
{
}

CPopUpScrollList::~CPopUpScrollList()
{
}

BEGIN_MESSAGE_MAP(CPopUpScrollList, CListCtrlCellWnd)
	//{{AFX_MSG_MAP(CPopUpScrollList)
	ON_WM_PAINT()
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONUP()
	ON_WM_MOUSEMOVE()
	ON_WM_VSCROLL()
	ON_WM_KEYDOWN()
	ON_WM_MOUSEWHEEL()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CPopUpScrollList message handlers
void CPopUpScrollList::ResetScrollInfo()
{
	SCROLLINFO si;
	si.cbSize = sizeof(si); 
	si.fMask  = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS; 
	si.nMin   = 0; 
	si.nMax   = m_pCellComboBox->m_astrItems.GetSize() - 1; 
	si.nPage  = m_pCellComboBox->m_lDisplayedRows; 
	si.nPos   = 0; 
	m_ScrollBar.SetScrollInfo(&si);  
}

void CPopUpScrollList::Create(HWND hParentWnd, CCellDropDown *pCellDropDown)
{
	CString szClassName = AfxRegisterWndClass(CS_CLASSDC|CS_SAVEBITS|CS_HREDRAW|CS_VREDRAW|CS_PARENTDC,
                                              0,
                                              (HBRUSH) (COLOR_BTNFACE+1), 
                                              0);

	const RECT rect = {0,0,0,0};

	m_hMainCtrlWnd = hParentWnd;

	CreateEx(0, szClassName, _T(""), WS_POPUP, rect, FromHandle(hParentWnd)->GetParent(), 0, NULL);
	
	m_pCellComboBox = dynamic_cast<CCellComboBox *>(pCellDropDown);
	ASSERT(m_pCellComboBox);

	RECT					rcBounding;
	SetRectEmpty(&rcBounding);
	m_ScrollBar.Create(WS_CHILD | WS_VISIBLE | SBS_VERT | SBS_RIGHTALIGN, rcBounding, this , 0);

	ResetScrollInfo();
}

void CPopUpScrollList::OnPaint() 
{
	CPaintDC dc(this); // device context for painting
	RECT							rcBounding,
									rcTxt,
									rcTxt2;
	CBrush							Brush, BrushSelected, *pBrush;
	LOGBRUSH						LogBrush;
	
	GetClientRect(&rcBounding);
	
	LogBrush.lbStyle = BS_NULL;
	Brush.CreateBrushIndirect(&LogBrush);
	dc.SelectObject(Brush);

	CPen	Pen = CPen(PS_SOLID, 0, IsAppThemed()? RGB(0xAA, 0xAA, 0xAA) : RGB(0, 0, 0));
	dc.SelectObject(Pen);

	dc.Rectangle(&rcBounding);
	Pen.DeleteObject();
	
	Brush.DeleteObject();

	InflateRect(&rcBounding, -::GetSystemMetrics(SM_CXBORDER), -::GetSystemMetrics(SM_CYBORDER));

	if (ShowScrollBar())
	{
		m_ScrollBar.ShowWindow(SW_NORMAL);
		m_ScrollBar.SetWindowPos(NULL, rcBounding.right - ::GetSystemMetrics(SM_CXVSCROLL), rcBounding.top, ::GetSystemMetrics(SM_CXVSCROLL), rcBounding.bottom - rcBounding.top, SWP_NOZORDER | SWP_SHOWWINDOW);
	}
	m_ScrollBar.ShowScrollBar(ShowScrollBar());

	CopyRect(&rcTxt, &rcBounding);

	CWnd *pParentWnd = this->GetParent();
	dc.SelectObject(pParentWnd->GetFont());

	LONG lItems = min(m_pCellComboBox->m_astrItems.GetSize(), m_pCellComboBox->m_lDisplayedRows);
	LONG lItemHeight = lItems? (rcBounding.bottom - rcBounding.top)/lItems : EMPTY_BOX_HEIGHT - 2 * ::GetSystemMetrics(SM_CYBORDER);

	rcTxt.bottom = rcTxt.top + lItemHeight; 

	dc.SetBkMode(TRANSPARENT);
	Brush.CreateSolidBrush(GetSysColor(COLOR_WINDOW));
	BrushSelected.CreateSolidBrush(GetSysColor(COLOR_HIGHLIGHT));

// If has scroll only
	if (ShowScrollBar())
		rcTxt.right -= ::GetSystemMetrics(SM_CXVSCROLL);

	int iScrollPos = (m_ScrollBar)? m_ScrollBar.GetScrollPos() : 0;

	if (!lItems)
		dc.FillRect(&rcTxt, &Brush);
	
	for (LONG lPos = m_lFirstPos; lPos < m_lFirstPos+lItems; lPos++)
	{
		pBrush = (lPos == m_lSelectedItem)? &BrushSelected : &Brush;
		dc.FillRect(&rcTxt, pBrush);
		CopyRect(&rcTxt2, &rcTxt);
		InflateRect(&rcTxt2, -::GetSystemMetrics(SM_CXFIXEDFRAME), 0);
		rcTxt2.top += ::GetSystemMetrics(SM_CYBORDER);
		m_pCellComboBox->DrawItem(&dc, &rcTxt2, m_pCellComboBox->m_astrItems[lPos+iScrollPos], lPos == m_lSelectedItem);
		OffsetRect(&rcTxt, 0, lItemHeight);
	}
	Brush.DeleteObject();
	BrushSelected.DeleteObject();
	
	// Do not call CWnd::OnPaint() for painting messages
}

void CPopUpScrollList::OnLButtonDown(UINT nFlags, CPoint point) 
{
	SelectAndClose();
	CListCtrlCellWnd::OnLButtonDown(nFlags, point);
}

void CPopUpScrollList::SelectAndClose()
{
	CWnd *pWnd = FromHandle(m_hMainCtrlWnd);

	::SendMessage(pWnd->m_hWnd, WM_CELL_CTRL_MSG, ID_POPUP_SELCLOSE, m_lSelectedItem + m_ScrollBar.GetScrollPos()); 
	ReleaseCapture();

	ShowWindow(SW_HIDE);
}

void CPopUpScrollList::OnLButtonUp(UINT nFlags, CPoint point) 
{
	CListCtrlCellWnd::OnLButtonUp(nFlags, point);
}

void CPopUpScrollList::OnMouseMove(UINT nFlags, CPoint point) 
{
	RECT rcTxt;
	BOOL bPtInRect = FALSE;

	m_pCellComboBox->ActivatePushButton(point);

	GetClientRect(&rcTxt);
	InflateRect(&rcTxt, -::GetSystemMetrics(SM_CXBORDER), -::GetSystemMetrics(SM_CYBORDER));

	if (m_ScrollBar)
		rcTxt.right -= ::GetSystemMetrics(SM_CXVSCROLL);

	LONG lItems = min(m_pCellComboBox->m_astrItems.GetSize(), m_pCellComboBox->m_lDisplayedRows);
	LONG lItemHeight = lItems? (rcTxt.bottom - rcTxt.top)/lItems : EMPTY_BOX_HEIGHT - 2 * ::GetSystemMetrics(SM_CYBORDER);
	rcTxt.bottom = rcTxt.top + lItemHeight;

	LONG lPos;
	for (lPos = m_lFirstPos; lPos < m_lFirstPos+lItems && !bPtInRect; lPos++)
	{
		bPtInRect = PtInRect(&rcTxt, point);
		OffsetRect(&rcTxt, 0, lItemHeight);
	}
	if (bPtInRect)
	{
		LONG lOldPos = m_lSelectedItem;
		m_lSelectedItem = --lPos;
		m_bActivated = TRUE;

		if (lOldPos != lPos)
		{
			GetClientRect(&rcTxt);
			InvalidateRect(&rcTxt, FALSE);
		}
	}
	CListCtrlCellWnd::OnMouseMove(nFlags, point);
}

void CPopUpScrollList::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) 
{
	SCROLLINFO si;
	pScrollBar->GetScrollInfo(&si);
	int iOldPos = si.nPos;

	switch (nSBCode)
	{
		case SB_LINEUP:				// Value 0 - top scroll button pressed
			si.nPos--;
		break;

		case SB_LINEDOWN:			// Value 1 - bottom scroll button pressed
			si.nPos++;
		break;

		case SB_PAGEUP:				// Value 2 - position above central button pressed
			si.nPos -= m_pCellComboBox->m_lDisplayedRows;
		break;

		case SB_PAGEDOWN:			// Value 3 - position below central button pressed
			si.nPos += m_pCellComboBox->m_lDisplayedRows;
		break;

		case SB_THUMBTRACK:			// Value 5 - if press on central button, this called 1st time
			si.nPos = si.nTrackPos;
		break;

		case SB_THUMBPOSITION:		// Value 4 - ... this called second time.		
		break;

		case SB_ENDSCROLL:			// Value 8 - This always called after any of the other SB codes. 
		break;

		default:
			ASSERT(!_T("Unrecognised SB code."));
			// the nSBCode SB_TOP (=6) and SB_BOTTOM (=7) not dealt with. ASSERT should show if required.
	}
	pScrollBar->SetScrollInfo(&si); // There is boundary correcting code in SetScrollInfo - so need not bother doing it ourselves.
	pScrollBar->GetScrollInfo(&si); // ...but need to collect again the maybe corrected si.nPos for setting selected item.
	
	m_lSelectedItem += iOldPos - si.nPos;

	this->Invalidate(FALSE);
	CListCtrlCellWnd::OnVScroll(nSBCode, nPos, pScrollBar);
}

BOOL CPopUpScrollList::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
{
	int nScrollPos = m_ScrollBar.GetScrollPos();

	if (zDelta > 0)// WHEEL_DELTA = 120. zDelta may be 240 for instance.
		nScrollPos--; 
	else if (zDelta < 0)
		nScrollPos++;
	else
		ASSERT(!_T("zDelta value not taken care of."));

	m_ScrollBar.SetScrollPos(nScrollPos);
	int nScrollPos2 = m_ScrollBar.GetScrollPos();

	if (nScrollPos == nScrollPos2)
		m_lSelectedItem += zDelta/WHEEL_DELTA;
	
	Invalidate(FALSE);
	return TRUE;
}

void CPopUpScrollList::OnOpenDropDown()
{
	m_lSelectedItem = m_pCellComboBox->m_lSelectedItem;
	
	if (m_lSelectedItem >= m_pCellComboBox->m_lDisplayedRows)
	{
		int nScrollPos = m_lSelectedItem - m_pCellComboBox->m_lDisplayedRows + 1;
		m_lSelectedItem -= nScrollPos;	
		m_ScrollBar.SetScrollPos(nScrollPos);
	}
	else
		m_ScrollBar.SetScrollPos(0);
}

void CPopUpScrollList::OnEnter()
{
	SelectAndClose();
}

void CPopUpScrollList::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	BOOL bRetVal = TRUE;
	int nScrollPos = m_ScrollBar.GetScrollPos();

	switch (nChar)
	{
		case VK_PRIOR:
			if (m_lSelectedItem != 0)
				m_lSelectedItem = 0;
			else
				nScrollPos -= m_pCellComboBox->m_lDisplayedRows;
		break;

		case VK_NEXT:
			if (m_lSelectedItem < m_pCellComboBox->m_lDisplayedRows - 1)
				m_lSelectedItem = m_pCellComboBox->m_lDisplayedRows - 1;
			else
				nScrollPos += m_pCellComboBox->m_lDisplayedRows;
		break;

		case VK_END:
			nScrollPos = m_pCellComboBox->m_astrItems.GetSize() - m_pCellComboBox->m_lDisplayedRows;
		break;

		case VK_HOME:
			nScrollPos = 0;
		break;

		case VK_UP:
			(m_lSelectedItem)? m_lSelectedItem-- : nScrollPos--;
		break;

		case VK_DOWN:
			if (m_lSelectedItem < m_pCellComboBox->m_lDisplayedRows - 1)
				m_lSelectedItem++;
			else
				nScrollPos++;
		break;
	}
	m_ScrollBar.SetScrollPos(nScrollPos);
	Invalidate(TRUE);

	CListCtrlCellWnd::OnKeyDown(nChar, nRepCnt, nFlags);
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United Kingdom United Kingdom
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions