📄 xlistctrl.cpp
字号:
// 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);
m_nSelItem = -1;
m_nSelSubItem = -1;
m_parrPdcs = NULL;
}
///////////////////////////////////////////////////////////////////////////////
// dtor
CXListCtrl::~CXListCtrl()
{
#ifndef DO_NOT_INCLUDE_XCOMBOLIST
if (m_pListBox)
delete m_pListBox;
#endif
if( m_parrPdcs )
m_parrPdcs = NULL;
}
///////////////////////////////////////////////////////////////////////////////
// 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* pNMHDR, LRESULT* pResult)
{
#ifndef DO_NOT_INCLUDE_XCOMBOLIST
UnpressComboButton();
#endif
//////////////////////////////////////////////////////////////////////////
// HXG
// 得到点击项
*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);
if( m_parrPdcs != NULL )
DrawPath( nItem, nSubItem, pLVCD->nmcd.hdc, 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++)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -