📄 reportctrl.cpp
字号:
#include "stdafx.h"
#include "ReportCtrl.h"
#include <afxtempl.h>
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
///////////////////////////////////////////////////////////////////////////////
// ReportCtrl.cpp
//
// Written by Bin Liu (abinn32@163.com)
//
// Special notes: The sorting methods was inspired by Mark Jackson in his "Sort List
// Control" article on http://www.codeproject.com
//
// This file is the implementation of class "CReportCtrl", which is derived from MFC class
// "CListCtrl" and specialized in the "Report" style list control manipulation.
//
// A bunch of methods are implemented or overloaded in this class in order to provide
// fast, efficient and convenient access and operations. For example, it is no more
// necessary to convert all other data types into character strings before
// using "SetItemText", you can pass in whatever into "SetItemText" because
// this class overloaded "SetItemText" for all common data types.
//
// Moreover, using heap pointers as list item data has become more safer since
// the user defined CALLBACK function "BOOL (*) (DWORD)" will be called every time
// _before_ a list item is actually being deleted, therefore you can perform the
// cleanup period to the deletion of each item.
//
// This code may be used in compiled form in any way you desire. This file may be
// redistributed unmodified by any means PROVIDING it is not sold for profit without
// the authors written consent, and providing that this notice and the authors name
// is included. If the source code in this file is used in any commercial application
// then acknowledgement must be made to the author of this file .
//
// This file is provided "as is" with no expressed or implied warranty.
//
// Special notes: The sorting methods was inspired by Mark Jackson in his "Sort List
// Control" article on http://www.codeproject.com
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
// The item data class, stores item data and item text array for sorting
///////////////////////////////////////////////////////////////////////////
class ItemData
{
public:
ItemData() : dwData(0) {}
CStringArray arrStr; // item texts
DWORD dwData; // the actual item data
};
////////////////////////////////////////////////////////////////////////////
// CReportHeaderCtrl implementation
////////////////////////////////////////////////////////////////////////////
/*
CReportCtrl::CReportHeaderCtrl::CReportHeaderCtrl()
{
m_iSortColumn = -1;
m_bSortAscending = 0;
}
int CReportCtrl::CReportHeaderCtrl::GetSortedColumn() const
{
return m_iSortColumn;
}
int CReportCtrl::CReportHeaderCtrl::IsSortAscending() const
{
return m_bSortAscending;
}
void CReportCtrl::CReportHeaderCtrl::SetSortAscending(int bAscending)
{
m_bSortAscending = bAscending;
}
void CReportCtrl::CReportHeaderCtrl::SetSortedColumn(int nCol)
{
m_iSortColumn = nCol;
}
////////////////////////////////////////////////////////////////////////////////
// Mark Jackson's code
////////////////////////////////////////////////////////////////////////////////
void CReportCtrl::CReportHeaderCtrl::UpdateSortArrow()
{
// change the item to owner drawn.
HD_ITEM hditem;
hditem.mask = HDI_FORMAT;
VERIFY(GetItem(m_iSortColumn, &hditem));
hditem.fmt |= HDF_OWNERDRAW;
VERIFY(SetItem(m_iSortColumn, &hditem));
// invalidate the header control so it gets redrawn
Invalidate();
}
////////////////////////////////////////////////////////////////////////////////
// Mark Jackson's code
////////////////////////////////////////////////////////////////////////////////
void CReportCtrl::CReportHeaderCtrl::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
// attath to the device context.
CDC dc;
VERIFY(dc.Attach(lpDrawItemStruct->hDC));
// save the device context.
const int iSavedDC = dc.SaveDC();
// get the column rect.
CRect rc(lpDrawItemStruct->rcItem);
// set the clipping region to limit drawing within the column.
CRgn rgn;
VERIFY(rgn.CreateRectRgnIndirect(&rc));
(void)dc.SelectObject(&rgn);
VERIFY(rgn.DeleteObject());
// draw the background,
CBrush brush(GetSysColor(COLOR_3DFACE));
// CBrush brush(RGB(255,0,0));
dc.FillRect(rc, &brush);
// get the column text and format.
TCHAR szText[ 256 ];
HD_ITEM hditem;
hditem.mask = HDI_TEXT | HDI_FORMAT;
hditem.pszText = szText;
hditem.cchTextMax = 255;
VERIFY(GetItem(lpDrawItemStruct->itemID, &hditem));
// determine the format for drawing the column label.
UINT uFormat = DT_SINGLELINE | DT_NOPREFIX | DT_NOCLIP | DT_VCENTER | DT_END_ELLIPSIS ;
if(hditem.fmt & HDF_CENTER)
uFormat |= DT_CENTER;
else if(hditem.fmt & HDF_RIGHT)
uFormat |= DT_RIGHT;
else
uFormat |= DT_LEFT;
// adjust the rect if the mouse button is pressed on it.
if(lpDrawItemStruct->itemState == ODS_SELECTED)
{
rc.left++;
rc.top += 2;
rc.right++;
}
CRect rcIcon(lpDrawItemStruct->rcItem);
const int iOffset = (rcIcon.bottom - rcIcon.top) / 4;
// adjust the rect further if the sort arrow is to be displayed.
if(lpDrawItemStruct->itemID == (UINT)m_iSortColumn)
rc.right -= 3 * iOffset;
rc.left += iOffset;
rc.right -= iOffset;
dc.SetBkMode(TRANSPARENT);
// draw the column label.
if(rc.left < rc.right)
(void)dc.DrawText(szText, -1, rc, uFormat);
// draw the sort arrow.
if(lpDrawItemStruct->itemID == (UINT)m_iSortColumn)
{
// set up the pens to use for drawing the arrow.
CPen penLight(PS_SOLID, 1, GetSysColor(COLOR_3DHILIGHT));
CPen penShadow(PS_SOLID, 1, GetSysColor(COLOR_3DSHADOW));
CPen* pOldPen = dc.SelectObject(&penLight);
if(m_bSortAscending > 0)
{
// draw the arrow pointing upwards.
dc.MoveTo(rcIcon.right - 2 * iOffset, iOffset);
dc.LineTo(rcIcon.right - iOffset, rcIcon.bottom - iOffset - 1);
dc.LineTo(rcIcon.right - 3 * iOffset - 2, rcIcon.bottom - iOffset - 1);
(void)dc.SelectObject(&penShadow);
dc.MoveTo(rcIcon.right - 3 * iOffset - 1, rcIcon.bottom - iOffset - 1);
dc.LineTo(rcIcon.right - 2 * iOffset, iOffset - 1);
}
else if(m_bSortAscending < 0)
{
// draw the arrow pointing downwards.
dc.MoveTo(rcIcon.right - iOffset - 1, iOffset);
dc.LineTo(rcIcon.right - 2 * iOffset - 1, rcIcon.bottom - iOffset);
(void)dc.SelectObject(&penShadow);
dc.MoveTo(rcIcon.right - 2 * iOffset - 2, rcIcon.bottom - iOffset);
dc.LineTo(rcIcon.right - 3 * iOffset - 1, iOffset);
dc.LineTo(rcIcon.right - iOffset - 1, iOffset);
}
// restore the pen.
(void)dc.SelectObject(pOldPen);
}
//exit01:
// restore the previous device context.
VERIFY(dc.RestoreDC(iSavedDC));
// detach the device context before returning.
(void)dc.Detach();
}
*/
////////////////////////////////////////////////////////////////////////////
// CReportCtrl implementation
////////////////////////////////////////////////////////////////////////////
CReportCtrl::CReportCtrl()
{
}
CReportCtrl::~CReportCtrl()
{
}
BEGIN_MESSAGE_MAP(CReportCtrl, CListCtrlBase)
//{{AFX_MSG_MAP(CReportCtrl)
ON_NOTIFY_REFLECT(LVN_COLUMNCLICK, OnColumnClick)
ON_WM_DESTROY()
ON_WM_VSCROLL()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CReportCtrl message handlers
void CReportCtrl::PreSubclassWindow()
{
// the list control must have the report style.
ASSERT(GetStyle() & LVS_REPORT);
CListCtrlBase::PreSubclassWindow();
VERIFY(m_wndHeader.SubclassWindow(GetHeaderCtrl()->GetSafeHwnd()));
// I can't see any reason that a report list does not use LVS_EX_FULLROWSELECT style,
// but of course, user can remove this style by "SetFullRowSelect(FALSE)" any time.
SetFullRowSelect(TRUE);
}
BOOL CReportCtrl::_IsValidIndex(int nIndex) const
{
return nIndex >= 0 && nIndex < CListCtrl::GetItemCount();
}
////////////////////////////////////////////////////////////////////////////////
// Mark Jackson's code
////////////////////////////////////////////////////////////////////////////////
BOOL CReportCtrl::SetHeadings(UINT uiStringID)
{
CString strHeadings;
VERIFY(strHeadings.LoadString(uiStringID));
return SetHeadings(strHeadings);
}
////////////////////////////////////////////////////////////////////////////////
// Mark Jackson's code, modified by Bin Liu
////////////////////////////////////////////////////////////////////////////////
// the heading text is in the format column 1 text,column 1 width;column 2 text,column 3 width;etc.
BOOL CReportCtrl::SetHeadings(const CString& strHeadings)
{
CString sPara;
int nParaStart = 0;
int nSemiColon = -1;
BOOL bContinue = TRUE;
while (nSemiColon < strHeadings.GetLength())
{
// determine the paragraph ("xxx,xxx,xxx;")
nSemiColon = strHeadings.Find(_T(';'), nParaStart);
if(nSemiColon == -1)
{
// reached the end of string
nSemiColon = strHeadings.GetLength();
bContinue = FALSE;
}
sPara = strHeadings.Mid(nParaStart, nSemiColon - nParaStart);
nParaStart = nSemiColon + 1; // ready to move on to next paragraph
int iStart = 0;
int iComma = -1;
// find the heading name string
iComma = sPara.Find(_T(','), iStart);
if(iComma == -1)
break;
const CString sColName = sPara.Mid(iStart, iComma - iStart);
iStart = iComma + 1;
// find the heading format string (0=LVCFMT_LEFT, 1=LVCFMT_CENTER, 2=LVCFMT_RIGHT)
int nFmt = LVCFMT_LEFT;
iComma = sPara.Find(_T(','), iStart);
if (iComma == -1)
{
// user does not specify a format, so use LVCFMT_LEFT as default)
}
else
{
// there is a format string
int n = _ttoi(sPara.Mid(iStart, iComma - iStart));
if (n == 1)
nFmt = LVCFMT_CENTER;
else if (n == 2)
nFmt = LVCFMT_RIGHT;
else
nFmt = LVCFMT_LEFT;
iStart = iComma + 1;
}
// remained is the column width string
int iWidth = _ttoi(sPara.Mid(iStart, sPara.GetLength() - iStart));
if (iWidth < 1)
iWidth = 1; // width should be at least 1
if(InsertColumn(m_wndHeader.GetItemCount(), sColName, nFmt, iWidth) == -1)
return FALSE;
}
return TRUE;
}
int CReportCtrl::InsertItemEx(int nIndex, LPCTSTR pszText, ...)
{
const int iIndex = CListCtrl::InsertItem(nIndex, pszText);
if (!_IsValidIndex(iIndex))
return iIndex;
CStringArray arr;
arr.Add(pszText);
va_list list;
va_start(list, pszText);
for(int iColumn = 1; iColumn < GetColumnCount(); iColumn++)
{
LPCTSTR lpsz = va_arg(list, LPCTSTR);
CString str = (lpsz == NULL) ? _T("") : lpsz;
arr.Add(str);
CListCtrl::SetItemText(iIndex, iColumn, str);
}
va_end(list);
_AssignNewItemData(iIndex, arr.GetData(), arr.GetSize());
return iIndex;
}
void CReportCtrl::_FreeItemMemory(const int iItem)
{
ASSERT(_IsValidIndex(iItem));
ItemData* pid = reinterpret_cast<ItemData*>(CListCtrl::GetItemData(iItem));
if (pid)
{
delete pid;
pid = NULL;
}
VERIFY(CListCtrl::SetItemData(iItem, NULL));
}
BOOL CReportCtrl::DeleteItem(int iItem)
{
if (!_IsValidIndex(iItem))
return FALSE;
// if (lpFunc != NULL && !lpFunc(GetItemData(iItem), lParam))
// return FALSE;
DWORD dwData = CListCtrl::GetItemData(iItem);
if (CListCtrl::DeleteItem(iItem))
{
// Free memory
ItemData* pData = reinterpret_cast<ItemData*>(dwData);
if (pData != NULL)
delete pData;
return TRUE;
}
else
{
return FALSE;
}
}
BOOL CReportCtrl::DeleteItem(int iItem, BOOL bSelectNextItem, ITEMDATAPROC lpFunc, LPARAM lParam)
{
if (!_IsValidIndex(iItem))
return FALSE;
if (lpFunc != NULL && !lpFunc(GetItemData(iItem), lParam))
return FALSE;
DWORD dwData = CListCtrl::GetItemData(iItem);
if (CListCtrl::DeleteItem(iItem))
{
// Free memory
ItemData* pData = reinterpret_cast<ItemData*>(dwData);
if (pData != NULL)
delete pData;
if (bSelectNextItem)
SelectItem(iItem, TRUE);
return TRUE;
}
else
{
return FALSE;
}
}
BOOL CReportCtrl::DeleteAllItems()
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -