📄 xlistctrl.cpp
字号:
// XListCtrl.cpp Version 1.4 - article available at www.codeproject.com
//
// Author: Hans Dietrich
// hdietrich@gmail.com
//
//..................................................................
// 修改者:匡奕胤 (kuan)
// yiyinkuang@gmail.com
//
// 修改内容:源码中有注释 add by kuan
// 修改结果: SetComboBox 函数中CStringArray *psa参数可支持stack变量
//...................................................................
//
// History
// Version 1.4 - 2006 September 1
// - See article for changes
//
// Version 1.3 - 2005 February 9
// - See article for changes
//
// Version 1.0 - 2002 February 4
// - Initial public release
//
// License:
// This software is released into the public domain. You are free to use
// it in any way you like, except that you may not sell this source code.
//
// 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"
#include "SortCStringArray.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/*
XLISTCTRLLIBDLLEXPORT UINT WM_XLISTCTRL_COMBO_SELECTION = ::RegisterWindowMessage(_T("WM_XLISTCTRL_COMBO_SELECTION"));
XLISTCTRLLIBDLLEXPORT UINT WM_XLISTCTRL_EDIT_END = ::RegisterWindowMessage(_T("WM_XLISTCTRL_EDIT_END"));
XLISTCTRLLIBDLLEXPORT UINT WM_XLISTCTRL_CHECKBOX_CLICKED = ::RegisterWindowMessage(_T("WM_XLISTCTRL_CHECKBOX_CLICKED"));
*/
UINT WM_XLISTCTRL_COMBO_SELECTION = ::RegisterWindowMessage(_T("WM_XLISTCTRL_COMBO_SELECTION"));
UINT WM_XLISTCTRL_EDIT_END = ::RegisterWindowMessage(_T("WM_XLISTCTRL_EDIT_END"));
UINT 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()
ON_WM_ERASEBKGND()
ON_WM_KEYDOWN()
ON_WM_RBUTTONDOWN()
ON_WM_NCLBUTTONDOWN()
//}}AFX_MSG_MAP
#ifndef DO_NOT_INCLUDE_XCOMBOLIST
ON_WM_TIMER()
ON_REGISTERED_MESSAGE(WM_XCOMBOLIST_VK_ESCAPE, OnComboEscape)
ON_REGISTERED_MESSAGE(WM_XCOMBOLIST_COMPLETE, OnComboComplete)
#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
ON_REGISTERED_MESSAGE(WM_XEDIT_KILL_FOCUS, OnXEditKillFocus)
ON_REGISTERED_MESSAGE(WM_XEDIT_VK_ESCAPE, OnXEditEscape)
END_MESSAGE_MAP()
///////////////////////////////////////////////////////////////////////////////
// ctor
CXListCtrl::CXListCtrl()
{
#ifndef DO_NOT_INCLUDE_XCOMBOLIST
m_bComboIsClicked = FALSE;
m_nComboItem = 0;
m_nComboSubItem = 0;
m_pCombo = NULL;
m_bFontIsCreated = FALSE;
#endif
m_dwExtendedStyleX = 0;
m_bHeaderIsSubclassed = FALSE;
m_bUseEllipsis = TRUE; //+++
m_bListModified = FALSE; //+++
m_bInitialCheck = FALSE;
m_strInitialString = _T("");
m_nPadding = 5; //+++
m_pEdit = NULL; //+++
m_nEditItem = 0; //+++
m_nEditSubItem = 0; //+++
GetColors();
}
///////////////////////////////////////////////////////////////////////////////
// dtor
CXListCtrl::~CXListCtrl()
{
#ifndef DO_NOT_INCLUDE_XCOMBOLIST
if (m_pCombo)
delete m_pCombo;
#endif
if (m_pEdit)
delete m_pEdit;
}
///////////////////////////////////////////////////////////////////////////////
// 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;
m_HeaderCtrl.SetListCtrl(this);
}
}
///////////////////////////////////////////////////////////////////////////////
// OnClick
BOOL CXListCtrl::OnClick(NMHDR* pNMHDR, LRESULT* pResult)
{
//XLISTCTRL_TRACE(_T("in CXListCtrl::OnClick\n"));
pNMHDR = pNMHDR;
*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.
}
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);
if (rect.IsRectEmpty())
{
return;
}
rect.bottom -= 1;
rect.left += 1; // leave margin in case row is highlighted
// fill interior with light gray
pDC->FillSolidRect(rect, RGB(224,224,224));
// draw border
pDC->Draw3dRect(&rect, RGB(0,0,0), m_crBtnShadow);
if (pXLCD[nSubItem].nProgressPercent > 0)
{
// draw progress bar and text
CRect LeftRect, RightRect;
LeftRect = rect;
LeftRect.left += 1;
LeftRect.top += 1;
LeftRect.bottom -= 1;
RightRect = LeftRect;
int w = (LeftRect.Width() * pXLCD[nSubItem].nProgressPercent) / 100;
LeftRect.right = LeftRect.left + w - 1;
RightRect.left = LeftRect.right;
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);
CRgn rgn;
rgn.CreateRectRgn(LeftRect.left, LeftRect.top, LeftRect.right,
LeftRect.bottom);
pDC->SelectClipRgn(&rgn);
pDC->SetTextColor(m_crHighLightText);//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);
}
}
}
///////////////////////////////////////////////////////////////////////////////
// DrawCheckbox
void CXListCtrl::DrawCheckbox(int nItem,
int nSubItem,
CDC *pDC,
COLORREF crText,
COLORREF crBkgnd,
CRect& rect,
XLISTCTRLDATA *pXLCD)
{
ASSERT(pDC);
ASSERT(pXLCD);
if (rect.IsRectEmpty())
{
return;
}
GetDrawColors(nItem, nSubItem, crText, crBkgnd);
pDC->FillSolidRect(&rect, crBkgnd);
CRect chkboxrect;
chkboxrect = rect;
chkboxrect.bottom -= 1;
chkboxrect.left += 9; // line up checkbox with header checkbox
chkboxrect.right = chkboxrect.left + chkboxrect.Height(); // width = height
CString str;
str = GetItemText(nItem, nSubItem);
if (str.IsEmpty())
{
// center the checkbox
chkboxrect.left = rect.left + rect.Width()/2 - chkboxrect.Height()/2 - 1;
chkboxrect.right = chkboxrect.left + chkboxrect.Height();
}
// fill rect around checkbox with white
pDC->FillSolidRect(&chkboxrect, m_crWindow);
// draw border
CBrush brush(RGB(51,102,153));
pDC->FrameRect(&chkboxrect, &brush);
if (pXLCD[nSubItem].nCheckedState == 1)
{
CPen *pOldPen = NULL;
CPen graypen(PS_SOLID, 1, m_crGrayText);
CPen blackpen(PS_SOLID, 1, RGB(51,153,51));
if (pXLCD[0].bEnabled)
pOldPen = pDC->SelectObject(&blackpen);
else
pOldPen = pDC->SelectObject(&graypen);
// draw the checkmark
int x = chkboxrect.left + 9;
ASSERT(x < chkboxrect.right);
int y = chkboxrect.top + 3;
int i;
for (i = 0; i < 4; i++)
{
pDC->MoveTo(x, y);
pDC->LineTo(x, y+3);
x--;
y++;
}
for (i = 0; i < 3; i++)
{
pDC->MoveTo(x, y);
pDC->LineTo(x, y+3);
x--;
y--;
}
if (pOldPen)
pDC->SelectObject(pOldPen);
}
if (!str.IsEmpty())
{
pDC->SetBkMode(TRANSPARENT);
pDC->SetTextColor(crText);
pDC->SetBkColor(crBkgnd);
CRect textrect;
textrect = rect;
textrect.left = chkboxrect.right + 4;
UINT nFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE; //+++
if (m_bUseEllipsis)
nFormat |= DT_END_ELLIPSIS;
pDC->DrawText(str, &textrect, nFormat);
}
}
///////////////////////////////////////////////////////////////////////////////
// GetDrawColors
void CXListCtrl::GetDrawColors(int nItem,
int nSubItem,
COLORREF& colorText,
COLORREF& colorBkgnd)
{
DWORD dwStyle = GetStyle();
DWORD dwExStyle = GetExtendedStyle();
COLORREF crText = colorText;
COLORREF crBkgnd = colorBkgnd;
if (GetItemState(nItem, LVIS_SELECTED))
{
if (dwExStyle & LVS_EX_FULLROWSELECT)
{
// selected? if so, draw highlight background
crText = m_crHighLightText;
crBkgnd = m_crHighLight;
// has focus? if not, draw gray background
if (m_hWnd != ::GetFocus())
{
if (dwStyle & LVS_SHOWSELALWAYS)
{
crText = m_crWindowText;
crBkgnd = m_crBtnFace;
}
else
{
crText = colorText;
crBkgnd = colorBkgnd;
}
}
}
else // not full row select
{
if (nSubItem == 0)
{
// selected? if so, draw highlight background
crText = m_crHighLightText;
crBkgnd = m_crHighLight;
// has focus? if not, draw gray background
if (m_hWnd != ::GetFocus())
{
if (dwStyle & LVS_SHOWSELALWAYS)
{
crText = m_crWindowText;
crBkgnd = m_crBtnFace;
}
else
{
crText = colorText;
crBkgnd = colorBkgnd;
}
}
}
}
}
colorText = crText;
colorBkgnd = crBkgnd;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -