⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 xlistctrl.cpp

📁 VC++开发的一个电话录音程序
💻 CPP
📖 第 1 页 / 共 4 页
字号:
// XListCtrl.cpp  Version 1.3
//
// Author:  Hans Dietrich
//          hdietrich2@hotmail.com
//
// This code is based on "Neat Stuff to do in List Controls Using Custom Draw"
// by Michael Dunn. See http://www.codeproject.com/listctrl/lvcustomdraw.asp
//
// Thanks to David Patrick for pointing out how to subclass header control
// if CXListCtrl is created via Create() instead of via dialog template.
//
// This software is released into the public domain.
// You are free to use it in any way you like.
//
// This software is provided "as is" with no expressed
// or implied warranty.  I accept no liability for any
// damage or loss of business that this software may cause.
//
///////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "XListCtrl.h"

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

UINT NEAR WM_XLISTCTRL_COMBO_SELECTION  = ::RegisterWindowMessage(_T("WM_XLISTCTRL_COMBO_SELECTION"));
UINT NEAR WM_XLISTCTRL_CHECKBOX_CLICKED = ::RegisterWindowMessage(_T("WM_XLISTCTRL_CHECKBOX_CLICKED"));

/////////////////////////////////////////////////////////////////////////////
// CXListCtrl

BEGIN_MESSAGE_MAP(CXListCtrl, CListCtrl)
	//{{AFX_MSG_MAP(CXListCtrl)
	ON_NOTIFY_REFLECT_EX(NM_CLICK, OnClick)
	ON_NOTIFY_REFLECT_EX(LVN_COLUMNCLICK, OnColumnClick)
	ON_WM_CREATE()
	ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomDraw)
	ON_WM_DESTROY()
	ON_WM_LBUTTONDOWN()
	ON_WM_PAINT()
	ON_WM_SYSCOLORCHANGE()
	//}}AFX_MSG_MAP
#ifndef DO_NOT_INCLUDE_XCOMBOLIST
	ON_WM_TIMER()
	ON_REGISTERED_MESSAGE(WM_XCOMBOLIST_VK_ESCAPE, OnComboEscape)
	ON_REGISTERED_MESSAGE(WM_XCOMBOLIST_VK_RETURN, OnComboReturn)
	ON_REGISTERED_MESSAGE(WM_XCOMBOLIST_KEYDOWN, OnComboKeydown)
	ON_REGISTERED_MESSAGE(WM_XCOMBOLIST_LBUTTONUP, OnComboLButtonUp)
#endif
#ifndef NO_XLISTCTRL_TOOL_TIPS
	ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipText)
	ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipText)
#endif
END_MESSAGE_MAP()


///////////////////////////////////////////////////////////////////////////////
// ctor
CXListCtrl::CXListCtrl()
{

#ifndef DO_NOT_INCLUDE_XCOMBOLIST
	m_bComboIsClicked       = FALSE;
	m_nComboItem            = 0;
	m_nComboSubItem         = 0;
	m_pListBox              = NULL;
	m_bFontIsCreated        = FALSE;
	m_strInitialComboString = _T("");
#endif

	m_dwExtendedStyleX      = 0;
	m_bHeaderIsSubclassed   = FALSE;

	m_cr3DFace              = ::GetSysColor(COLOR_3DFACE);
	m_cr3DHighLight         = ::GetSysColor(COLOR_3DHIGHLIGHT);
	m_cr3DShadow            = ::GetSysColor(COLOR_3DSHADOW);
	m_crBtnFace             = ::GetSysColor(COLOR_BTNFACE);
	m_crBtnShadow           = ::GetSysColor(COLOR_BTNSHADOW);
	m_crBtnText             = ::GetSysColor(COLOR_BTNTEXT);
	m_crGrayText            = ::GetSysColor(COLOR_GRAYTEXT);
	m_crHighLight           = ::GetSysColor(COLOR_HIGHLIGHT);
	m_crHighLightText       = ::GetSysColor(COLOR_HIGHLIGHTTEXT);
	m_crWindow              = ::GetSysColor(COLOR_WINDOW);
	m_crWindowText          = ::GetSysColor(COLOR_WINDOWTEXT);
}

///////////////////////////////////////////////////////////////////////////////
// dtor
CXListCtrl::~CXListCtrl()
{
#ifndef DO_NOT_INCLUDE_XCOMBOLIST
	if (m_pListBox)
		delete m_pListBox;
#endif
}

///////////////////////////////////////////////////////////////////////////////
// PreSubclassWindow
void CXListCtrl::PreSubclassWindow()
{
	CListCtrl::PreSubclassWindow();

	// for Dialog based applications, this is a good place
	// to subclass the header control because the OnCreate()
	// function does not get called.

	SubclassHeaderControl();
}

///////////////////////////////////////////////////////////////////////////////
// OnCreate
int CXListCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CListCtrl::OnCreate(lpCreateStruct) == -1)
	{
		ASSERT(FALSE);
		return -1;
	}

	// When the CXListCtrl object is created via a call to Create(), instead
	// of via a dialog box template, we must subclass the header control
	// window here because it does not exist when the PreSubclassWindow()
	// function is called.

	SubclassHeaderControl();

	return 0;
}

///////////////////////////////////////////////////////////////////////////////
// SubclassHeaderControl
void CXListCtrl::SubclassHeaderControl()
{
	if (m_bHeaderIsSubclassed)
		return;

	// if the list control has a header control window, then
	// subclass it

	// Thanks to Alberto Gattegno and Alon Peleg牋and their article
	// "A Multiline Header Control Inside a CListCtrl" for easy way
	// to determine if the header control exists.

	CHeaderCtrl* pHeader = GetHeaderCtrl();
	if (pHeader)
	{
		VERIFY(m_HeaderCtrl.SubclassWindow(pHeader->m_hWnd));
		m_bHeaderIsSubclassed = TRUE;
	}
}

///////////////////////////////////////////////////////////////////////////////
// OnClick
BOOL CXListCtrl::OnClick(NMHDR*, LRESULT* pResult)
{
#ifndef DO_NOT_INCLUDE_XCOMBOLIST
	UnpressComboButton();
#endif
	*pResult = 0;
	return FALSE;		// return FALSE to send message to parent also -
						// NOTE:  MSDN documentation is incorrect
}

///////////////////////////////////////////////////////////////////////////////
// OnCustomDraw
void CXListCtrl::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult)
{
	NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>(pNMHDR);

	// Take the default processing unless we set this to something else below.
	*pResult = CDRF_DODEFAULT;

	// First thing - check the draw stage. If it's the control's prepaint
	// stage, then tell Windows we want messages for every item.

	if (pLVCD->nmcd.dwDrawStage == CDDS_PREPAINT)
	{
		*pResult = CDRF_NOTIFYITEMDRAW;
	}
	else if (pLVCD->nmcd.dwDrawStage == CDDS_ITEMPREPAINT)
	{
		// This is the notification message for an item.  We'll request
		// notifications before each subitem's prepaint stage.

		*pResult = CDRF_NOTIFYSUBITEMDRAW;
	}
	else if (pLVCD->nmcd.dwDrawStage == (CDDS_ITEMPREPAINT | CDDS_SUBITEM))
	{
		// This is the prepaint stage for a subitem. Here's where we set the
		// item's text and background colors. Our return value will tell
		// Windows to draw the subitem itself, but it will use the new colors
		// we set here.

		int nItem = static_cast<int> (pLVCD->nmcd.dwItemSpec);
		int nSubItem = pLVCD->iSubItem;

		XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) pLVCD->nmcd.lItemlParam;
		ASSERT(pXLCD);

		COLORREF crText  = m_crWindowText;
		COLORREF crBkgnd = m_crWindow;

		if (pXLCD)
		{
			crText  = pXLCD[nSubItem].crText;
			crBkgnd = pXLCD[nSubItem].crBackground;

			if (!pXLCD[0].bEnabled)
				crText = m_crGrayText;
		}

		// store the colors back in the NMLVCUSTOMDRAW struct
		pLVCD->clrText = crText;
		pLVCD->clrTextBk = crBkgnd;

		CDC* pDC = CDC::FromHandle(pLVCD->nmcd.hdc);
		CRect rect;
		GetSubItemRect(nItem, nSubItem, LVIR_BOUNDS, rect);

		if (pXLCD && (pXLCD[nSubItem].bShowProgress))
		{
			DrawProgress(nItem, nSubItem, pDC, crText, crBkgnd, rect, pXLCD);

			*pResult = CDRF_SKIPDEFAULT;	// We've painted everything.
		}
#ifndef DO_NOT_INCLUDE_XCOMBOLIST
		else if (pXLCD && (pXLCD[nSubItem].bCombo))
		{
			if (GetItemState(nItem, LVIS_SELECTED))
				DrawComboBox(nItem, nSubItem, pDC, crText, crBkgnd, rect, pXLCD);
			else
				DrawText(nItem, nSubItem, pDC, crText, crBkgnd, rect, pXLCD);

			*pResult = CDRF_SKIPDEFAULT;	// We've painted everything.
		}
#endif
		else if (pXLCD && (pXLCD[nSubItem].nCheckedState != -1))
		{
			DrawCheckbox(nItem, nSubItem, pDC, crText, crBkgnd, rect, pXLCD);

			*pResult = CDRF_SKIPDEFAULT;	// We've painted everything.
		}
		else
		{
			rect.left += DrawImage(nItem, nSubItem, pDC, crText, crBkgnd, rect, pXLCD);

			DrawText(nItem, nSubItem, pDC, crText, crBkgnd, rect, pXLCD);

			*pResult = CDRF_SKIPDEFAULT;	// We've painted everything.
		}
	}
}

///////////////////////////////////////////////////////////////////////////////
// DrawProgress
void CXListCtrl::DrawProgress(int nItem,
							  int nSubItem,
							  CDC *pDC,
							  COLORREF crText,
							  COLORREF crBkgnd,
							  CRect& rect,
							  XLISTCTRLDATA *pXLCD)
{
	UNUSED_ALWAYS(nItem);

	ASSERT(pDC);
	ASSERT(pXLCD);

	rect.bottom -= 1;
	rect.left += 1;		// leave margin in case row is highlighted
	rect.right -= 2;

	// draw border

	CPen graypen(PS_SOLID, 1, m_crBtnShadow);
	CPen *pOldPen = pDC->SelectObject(&graypen);

	pDC->MoveTo(rect.left, rect.bottom);
	pDC->LineTo(rect.right+1, rect.bottom);

	pDC->MoveTo(rect.left, rect.top);
	pDC->LineTo(rect.right, rect.top);

	pDC->MoveTo(rect.left, rect.top);
	pDC->LineTo(rect.left, rect.bottom);

	pDC->MoveTo(rect.right, rect.top);
	pDC->LineTo(rect.right, rect.bottom);

	// fill interior with light gray
	CRect InteriorRect;
	InteriorRect = rect;
	InteriorRect.left += 1;
	InteriorRect.top += 1;
	pDC->FillSolidRect(InteriorRect, RGB(224,224,224));

	// finish drawing border
	CPen blackpen(PS_SOLID, 1, RGB(0,0,0));
	pDC->SelectObject(&blackpen);

	pDC->MoveTo(rect.left+1, rect.top+1);
	pDC->LineTo(rect.right, rect.top+1);

	pDC->MoveTo(rect.left+1, rect.top+1);
	pDC->LineTo(rect.left+1, rect.bottom);

	pDC->SelectObject(pOldPen);

	if (pXLCD[nSubItem].nProgressPercent > 0)
	{
		// draw progress bar and text

		CRect LeftRect, RightRect;
		LeftRect = rect;
		LeftRect.left += 2;
		LeftRect.top += 2;
		RightRect = LeftRect;
		int w = (LeftRect.Width() * pXLCD[nSubItem].nProgressPercent) / 100;
		LeftRect.right = LeftRect.left + w;
		RightRect.left = LeftRect.right + 1;
		pDC->FillSolidRect(LeftRect, m_crHighLight);

		if (pXLCD[nSubItem].bShowProgressMessage)
		{
			CString str, format;
			format = pXLCD[nSubItem].strProgressMessage;
			if (format.IsEmpty())
				str.Format(_T("%d%%"), pXLCD[nSubItem].nProgressPercent);
			else
				str.Format(format, pXLCD[nSubItem].nProgressPercent);

			pDC->SetBkMode(TRANSPARENT);

			CRect TextRect;
			TextRect = rect;
			TextRect.DeflateRect(1, 1);
			TextRect.top += 1;

			CRgn rgn;
			rgn.CreateRectRgn(LeftRect.left, LeftRect.top, LeftRect.right, LeftRect.bottom);
			pDC->SelectClipRgn(&rgn);
			pDC->SetTextColor(crBkgnd);
			pDC->DrawText(str, &TextRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);

			rgn.DeleteObject();
			rgn.CreateRectRgn(RightRect.left, RightRect.top, RightRect.right, RightRect.bottom);
			pDC->SelectClipRgn(&rgn);
			pDC->SetTextColor(crText);
			pDC->DrawText(str, &TextRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
			rgn.DeleteObject();
			pDC->SelectClipRgn(NULL);
		}
	}
}

#ifndef DO_NOT_INCLUDE_XCOMBOLIST

///////////////////////////////////////////////////////////////////////////////
// DrawComboBox
void CXListCtrl::DrawComboBox(int nItem,
							  int nSubItem,
							  CDC *pDC,
							  COLORREF crText,
							  COLORREF crBkgnd,
							  CRect& rect,
							  XLISTCTRLDATA *pXLCD)
{
	UNUSED_ALWAYS(crText);
	UNUSED_ALWAYS(crBkgnd);

	ASSERT(pDC);
	ASSERT(pXLCD);

#ifdef _DEBUG
	DWORD dwExStyle = GetExtendedStyle();
	if ((dwExStyle & LVS_EX_FULLROWSELECT) == 0)
	{
		TRACE(_T("XListCtrl: combo boxes require LVS_EX_FULLROWSELECT style\n"));
		ASSERT(FALSE);
	}
#endif

	rect.bottom += 1;	// bottom edge is white, so this doesn't matter
	rect.left += 1;		// leave margin in case row is highlighted
	rect.right -= 2;

	// draw border

	CPen pen(PS_SOLID, 1, m_crBtnShadow);
	CPen *pOldPen = pDC->SelectObject(&pen);

	pDC->MoveTo(rect.left, rect.bottom-2);
	pDC->LineTo(rect.right, rect.bottom-2);

	pDC->MoveTo(rect.left, rect.top);
	pDC->LineTo(rect.right, rect.top);

	pDC->MoveTo(rect.left, rect.top);
	pDC->LineTo(rect.left, rect.bottom-2);

	pDC->MoveTo(rect.right, rect.top);
	pDC->LineTo(rect.right, rect.bottom-1);

	CPen blackpen(PS_SOLID, 1, RGB(0,0,0));
	pDC->SelectObject(&blackpen);

	// fill interior with white
	CRect InteriorRect;
	InteriorRect = rect;
	InteriorRect.DeflateRect(2, 2);
	pDC->FillSolidRect(InteriorRect, RGB(255,255,255));

	// set arrow rect
	CRect ArrowRect;
	ArrowRect = rect;
	ArrowRect.right += 1;
	ArrowRect.left = ArrowRect.right - ArrowRect.Height();
	ArrowRect.DeflateRect(2, 2);

	CString str;
	str = GetItemText(nItem, nSubItem);

	if (str.IsEmpty())
	{
		// subitem text is empty, try to get from listbox strings
		if (pXLCD[nSubItem].psa)
		{
			int index = 0;
			if ((pXLCD[nSubItem].nInitialComboSel >= 0) &&
				(pXLCD[nSubItem].psa->GetSize() > pXLCD[nSubItem].nInitialComboSel))
			{
				index = pXLCD[nSubItem].nInitialComboSel;
				str = pXLCD[nSubItem].psa->GetAt(index);
				SetItemText(nItem, nSubItem, str);
			}
		}
	}

	if (!str.IsEmpty())
	{
		// draw text
		CRect TextRect;
		TextRect = rect;
		TextRect.top -= 1;
		TextRect.left += 2;
		TextRect.right = ArrowRect.left - 1;

		pDC->SetBkMode(TRANSPARENT);
		COLORREF cr = m_crWindowText;
		if (!pXLCD[0].bEnabled)
			cr = m_crGrayText;
		pDC->SetTextColor(cr);
		pDC->SetBkColor(m_crWindow);
		UINT nFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
		pDC->DrawText(str, &TextRect, nFormat);
	}

	if (!pXLCD[nSubItem].bComboIsClicked)
	{
		// draw depressed combobox
		pDC->DrawEdge(&ArrowRect, EDGE_RAISED, BF_RECT);
		ArrowRect.DeflateRect(2, 2);
		pDC->FillSolidRect(ArrowRect, m_crBtnFace);

		// draw the downarrow using blackpen
		int x = ArrowRect.left + 1;
		int y = ArrowRect.top + 2;
		int k = 5;
		for (int i = 0; i < 3; i++)
		{
			pDC->MoveTo(x, y);
			pDC->LineTo(x+k, y);
			x++;
			y++;
			k -= 2;
		}
	}
	else
	{
		// draw normal combobox
		m_rectComboButton = ArrowRect;
		CBrush brush(m_cr3DShadow);
		pDC->FrameRect(&ArrowRect, &brush);
		ArrowRect.DeflateRect(1, 1);
		pDC->FillSolidRect(ArrowRect, m_crBtnFace);

		// draw the downarrow using blackpen
		int x = ArrowRect.left + 3;
		int y = ArrowRect.top + 4;
		int k = 5;
		for (int i = 0; i < 3; i++)
		{
			pDC->MoveTo(x, y);
			pDC->LineTo(x+k, y);
			x++;
			y++;
			k -= 2;
		}

		// show listbox if not already shown
		if (!m_pListBox)
		{
			// create and populate the combo's listbox
			m_pListBox = new CXComboList(this);
			ASSERT(m_pListBox);

			if (m_pListBox)
			{
				m_nComboItem = nItem;
				m_nComboSubItem = nSubItem;

				m_rectComboList = rect;
				m_rectComboList.right -= 1;
				m_rectComboList.top += rect.Height() - 1;

				m_rectComboList.bottom = m_rectComboList.top +
					(pXLCD[nSubItem].nComboListHeight) * (rect.Height() - 2);
				ClientToScreen(&m_rectComboList);

				CString szClassName = AfxRegisterWndClass(CS_CLASSDC|CS_SAVEBITS,
														  LoadCursor(NULL, IDC_ARROW));

				BOOL bSuccess = m_pListBox->CreateEx(0, szClassName, _T(""),
									WS_POPUP | WS_VISIBLE /*| WS_VSCROLL*/ | WS_BORDER,
									m_rectComboList,
									this, 0, NULL);

				if (!bSuccess)
				{
				}
				else
				{
					m_strInitialComboString = _T("");

					if (!m_bFontIsCreated)
					{
						// use font from list control
						CFont *font = pDC->GetCurrentFont();
						if (font)
						{
							LOGFONT lf;
							font->GetLogFont(&lf);
							m_ListboxFont.CreateFontIndirect(&lf);
							m_bFontIsCreated = TRUE;
						}
					}

					if (m_bFontIsCreated)
						m_pListBox->SetFont(&m_ListboxFont, FALSE);

					if (pXLCD[nSubItem].psa)
					{
						CString s;
						for (int i = 0; i < pXLCD[nSubItem].psa->GetSize(); i++)
						{
							s = pXLCD[nSubItem].psa->GetAt(i);
							if (!s.IsEmpty())
								m_pListBox->AddString(s);
						}
					}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -