📄 reportctrl.cpp
字号:
///////////////////////////////////////////////////////////////////////////////
// ReportCtrl.cpp
//
// CReportCtrl, a CListCtrl derived class that is specialized on "Report View"
// style.
//
// Features:
//
// 1, Item sorting by clicking on column header.
// 2, Sub-item text edit.
// 3, Item repositioning.
// 4, Customizing checkbox styles, including "single" and "disabled".
// 5, Sending a message to parent window when user clicks on a checkbox.
// 6, Convenient item insertion, deletion, moving, and sub-item text changing.
// 7, Sub-item images and color
// 8, And much more...
//
// 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.
//
// Written by Bin Liu (abinn32@yahoo.com)
//
// History
//
// Nov. 26, 2003 - Initial release.
// Dec. 03, 2003 - Fixed a bug in "EndEdit" where item text were not preperly committed.
// - Completed the implementation of the "Sort-Separator" feature.
// Jan. 01, 2004 - Fixed a bug in "SetItemData".
// - Added message "WM_EDIT_COMMITTED" which is sent to the parent window
// when an item text editing is committed.
// - Fixed a bug in "SetItemText"(double type).
// - Fixed a bug where item sorting does not work properly when there
// are multiple CReportCtrl objects on same window.
//
///////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "ReportCtrl.h"
#include <afxtempl.h>
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
// Below styles MUST be present in a report ctrl
#define MUST_STYLE (WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL | LVS_REPORT)
#define MUST_EX_STYLE (LVS_EX_FULLROWSELECT | LVS_EX_SUBITEMIMAGES)
// Below styles MUST NOT be present in a report ctrl
#define MUST_NOT_STYLE (LVS_EDITLABELS | LVS_ICON | LVS_SMALLICON | LVS_LIST | LVS_NOSCROLL)
#define MUST_NOT_EX_STYLE (0)
struct ROWINFO
{
DWORD dwData;
DWORD dwStates;
CArray<int, int> aImages;
CStringArray aTexts;
};
//////////////////////////////////////////////////////////////////////////
// CItemData class is used for store extra information
//////////////////////////////////////////////////////////////////////////
class CItemData
{
public:
CItemData() { dwData = 0; }
void InsertColumn(int nColumn);
void DeleteColumn(int nColumn);
DWORD dwData; // The actual 32-bit user data stores here
CArray<COLORREF, COLORREF> aTextColors; // Sub item text colors
CArray<COLORREF, COLORREF> aBkColors; // Sub item backgroud colors
};
void CItemData::InsertColumn(int nColumn)
{
aTextColors.InsertAt(nColumn, ::GetSysColor(COLOR_WINDOWTEXT));
aBkColors.InsertAt(nColumn, ::GetSysColor(COLOR_WINDOW));
}
void CItemData::DeleteColumn(int nColumn)
{
aTextColors.RemoveAt(nColumn);
aBkColors.RemoveAt(nColumn);
}
///////////////////////////////////////////////////////////////////////
// A set of functions used for item text format determining
///////////////////////////////////////////////////////////////////////
namespace _ITEM_COMPARE_FUNCS
{
BOOL _IsDecNumber(const CString& str, double& f);
int _DecNumberCompare(double f1, double f2);
BOOL _IsHexNumber(const CString& str, DWORD& dw);
int _HexNumberCompare(DWORD dw1, DWORD dw2);
BOOL _IsDate(const CString& str, COleDateTime& date);
int _DateCompare(const COleDateTime& date1, const COleDateTime& date2);
};
BOOL _ITEM_COMPARE_FUNCS::_IsDecNumber(const CString& str, double& f)
{
if (str.IsEmpty())
return FALSE;
LPTSTR p;
f = _tcstod(str, &p);
return (*p == _T('\0') || (*p == _T('%') && p[1] == _T('\0')));
}
int _ITEM_COMPARE_FUNCS::_DecNumberCompare(double f1, double f2)
{
if(f1 < f2)
return -1;
if(f1 > f2)
return 1;
return 0;
}
BOOL _ITEM_COMPARE_FUNCS::_IsHexNumber(const CString& str, DWORD& dw)
{
if (str.IsEmpty())
return FALSE;
LPTSTR p;
dw = _tcstoul(str, &p, 16);
return *p == _T('\0');
}
int _ITEM_COMPARE_FUNCS::_HexNumberCompare(DWORD dw1, DWORD dw2)
{
if (dw1 > dw2)
return 1;
if (dw1 < dw2)
return -1;
return 0;
}
BOOL _ITEM_COMPARE_FUNCS::_IsDate(const CString& str, COleDateTime& date)
{
return date.ParseDateTime(str);
}
int _ITEM_COMPARE_FUNCS::_DateCompare(const COleDateTime& date1, const COleDateTime& date2)
{
if (date1 < date2)
return -1;
if (date1 > date2)
return 1;
return 0;
}
/////////////////////////////////////////////////////////////////////////////
// CReportCtrl Implementation
/////////////////////////////////////////////////////////////////////////////
CReportCtrl::CReportCtrl(): m_pWndEdit(NULL)
{
m_pWndEdit = new CEdit;
VERIFY(m_pWndEdit != NULL);
m_pszSeparator = NULL;
m_bAllowEdit = FALSE;
m_ptEditting.x = -1;
m_ptEditting.y = -1;
m_nChkStyle = RC_CHKBOX_NORMAL;
m_dwPrevEditFmt = ES_LEFT;
m_nSortCol = -1;
m_bSortAscending = TRUE;
}
CReportCtrl::~CReportCtrl()
{
if (m_pszSeparator != NULL)
delete [] m_pszSeparator;
if (m_pWndEdit != NULL)
delete m_pWndEdit;
CProgressEntry* pProgEntry=0;
int Index=0;
POSITION pos = m_ProgEntries.GetStartPosition();
while (pos != NULL)
{
m_ProgEntries.GetNextAssoc(pos, Index, pProgEntry);
if (pProgEntry) delete pProgEntry;
}
m_ProgEntries.RemoveAll();
}
BEGIN_MESSAGE_MAP(CReportCtrl, CListCtrl)
//{{AFX_MSG_MAP(CReportCtrl)
ON_NOTIFY_REFLECT(LVN_COLUMNCLICK, OnColumnclick)
ON_WM_LBUTTONDOWN()
ON_WM_CREATE()
ON_WM_DESTROY()
ON_WM_LBUTTONDBLCLK()
ON_WM_MBUTTONDOWN()
ON_WM_MBUTTONDBLCLK()
ON_WM_RBUTTONDOWN()
ON_WM_RBUTTONDBLCLK()
ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomDraw)
ON_WM_HSCROLL()
ON_WM_VSCROLL()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CReportCtrl message handlers
void CReportCtrl::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult)
{
LPNMLVCUSTOMDRAW lplvcd = (LPNMLVCUSTOMDRAW)pNMHDR;
if (lplvcd->nmcd.dwDrawStage == CDDS_PREPAINT)
{
*pResult = CDRF_NOTIFYITEMDRAW;
}
else if (lplvcd->nmcd.dwDrawStage == CDDS_ITEMPREPAINT)
{
*pResult = CDRF_NOTIFYSUBITEMDRAW;
}
else if ( lplvcd->nmcd.dwDrawStage == (CDDS_ITEMPREPAINT | CDDS_SUBITEM) )
{
CItemData* p = (CItemData*)(CListCtrl::GetItemData(lplvcd->nmcd.dwItemSpec));
ASSERT(p != NULL);
ASSERT(lplvcd->iSubItem >= 0 && lplvcd->iSubItem < p->aTextColors.GetSize());
lplvcd->clrText = p->aTextColors[lplvcd->iSubItem];
lplvcd->clrTextBk = p->aBkColors[lplvcd->iSubItem];
int nItem = lplvcd->nmcd.dwItemSpec;
int nSubItem = lplvcd->iSubItem;
DrawProgress(nItem,nSubItem,3,pResult);
return;
}
}
void CReportCtrl::DrawProgress(int nItem, int nSubItem, int index,LRESULT* pResult)
{
if (index != nSubItem)
{
*pResult = CDRF_DODEFAULT;
return;
}
CRect rcSubItem;
this->GetSubItemRect(nItem, nSubItem, LVIR_BOUNDS, rcSubItem);
CProgressEntry* ProgEntry = new CProgressEntry(nItem, nSubItem);
ProgEntry->m_Prog = (CProgressCtrlX*)this->GetItemData(nItem);
if (NULL == ProgEntry->m_Prog)
{
ProgEntry->m_Prog = new CProgressCtrlX;
if (rcSubItem.Width() > 100)
rcSubItem.right = rcSubItem.left + 100;
ProgEntry->m_Prog->Create(WS_CHILD|WS_VISIBLE|PBS_SMOOTH, rcSubItem,
this, 0x1000 + nItem);
ASSERT(ProgEntry->m_Prog->GetSafeHwnd());
ProgEntry->m_Prog->SetRange(0, 100);
ProgEntry->m_Prog->SetPos( nItem*10 % 100 );
ProgEntry->m_Prog->SetTextFormat("已完成: %d%%", PBS_SHOW_PERCENT);
ProgEntry->m_Prog->SetTextColor(RGB(255,255,255),RGB(0,0,0));
ProgEntry->m_Prog->SetGradientColors(RGB(25,25,121),RGB(255,255,0));
ProgEntry->m_Prog->SetBkColor(RGB(192,192,192));
this->SetItemData(nItem, (DWORD)ProgEntry->m_Prog);
}
if (rcSubItem.Width() > 100)
rcSubItem.right = rcSubItem.left + 100;
ProgEntry->m_Prog->MoveWindow(rcSubItem);
ProgEntry->m_Prog->ShowWindow(SW_SHOW);
m_ProgEntries[nItem] = ProgEntry;
*pResult = CDRF_SKIPDEFAULT;
}
void CReportCtrl::OnColumnclick(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
// TODO: Add your control notification handler code here
const int COL = pNMListView->iSubItem;
SortItems(COL, COL == m_nSortCol ? !m_bSortAscending : TRUE);
*pResult = 0;
}
void CReportCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
_MouseClkMonitor(WM_LBUTTONDOWN, nFlags, point, TRUE);
}
void CReportCtrl::OnLButtonDblClk(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
_MouseClkMonitor(WM_LBUTTONDBLCLK, nFlags, point, TRUE);
}
void CReportCtrl::OnMButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
_MouseClkMonitor(WM_MBUTTONDOWN, nFlags, point, FALSE);
}
void CReportCtrl::OnMButtonDblClk(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
_MouseClkMonitor(WM_MBUTTONDBLCLK, nFlags, point, FALSE);
}
void CReportCtrl::OnRButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
_MouseClkMonitor(WM_RBUTTONDOWN, nFlags, point, FALSE);
}
void CReportCtrl::OnRButtonDblClk(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
_MouseClkMonitor(WM_RBUTTONDBLCLK, nFlags, point, FALSE);
}
int CReportCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
lpCreateStruct->style &= ~MUST_NOT_STYLE;
lpCreateStruct->style |= MUST_STYLE;
if (CListCtrl::OnCreate(lpCreateStruct) == -1)
return -1;
// TODO: Add your specialized creation code here
SetExtendedStyle(GetExtendedStyle());
ASSERT(GetHeaderCtrl() != NULL);
return 0;
}
void CReportCtrl::OnDestroy()
{
DeleteAllItems();
m_pWndEdit->DestroyWindow();
m_imgList.DeleteImageList();
m_headerImgList.DeleteImageList();
int nCount = this->GetItemCount();
CProgressCtrl* pCtrl;
for(int i = 0; i < nCount; i++)
{
pCtrl = (CProgressCtrl*)this->GetItemData(i);
if (NULL != pCtrl)
delete pCtrl;
this->SetItemData(i, 0);
}
CListCtrl::OnDestroy();
// TODO: Add your message handler code here
}
BOOL CReportCtrl::_IsValidIndex(int nIndex) const
{
return nIndex >= 0 && nIndex < CListCtrl::GetItemCount();
}
// the heading text is in the format of "text,width,format;text,width,format;..."
BOOL CReportCtrl::SetColumnHeader(const CString& strHeadings)
{
DeleteAllItems();
DeleteAllColumns();
EndEdit(TRUE);
BOOL bInserted = FALSE;
CStringArray aLong, aShort;
_StringSplit(strHeadings, aLong, _T(';'));
for (int i = 0; i < aLong.GetSize(); i++)
{
_StringSplit(aLong[i], aShort, _T(','));
if (aShort.GetSize() > 0)
{
const int WIDTH = aShort.GetSize() > 1 ? _ttoi(aShort[1]) : 100;
int nFormat = aShort.GetSize() > 2 ? _ttoi(aShort[2]) : 0;
if (nFormat == 1)
nFormat = LVCFMT_CENTER;
else if (nFormat == 2)
nFormat = LVCFMT_RIGHT;
else
nFormat = LVCFMT_LEFT;
bInserted |= (InsertColumn(GetColumnCount(), aShort[0], nFormat, WIDTH) >= 0);
}
}
return bInserted;
}
int CReportCtrl::InsertItem(int nIndex, LPCTSTR lpText)
{
EndEdit(TRUE);
_UnsetSortedColumn();
const int IDX = CListCtrl::InsertItem(nIndex, lpText);
if (IDX >= 0)
_AllocItemMemory(IDX);
return IDX;
}
BOOL CReportCtrl::DeleteItem(int nItem, BOOL bSelectNextItem)
{
EndEdit(m_ptEditting.x != nItem);
if (bSelectNextItem)
SetItemStates(nItem + 1, RC_ITEM_SELECTED);
_FreeItemMemory(nItem);
return CListCtrl::DeleteItem(nItem);
}
int CReportCtrl::DeleteAllItems(DWORD dwStates)
{
EndEdit(FALSE);
int nItemCount = CListCtrl::GetItemCount();
if (dwStates & RC_ITEM_ALL)
{
LockWindowUpdate();
for (int i = 0; i < nItemCount; i++)
_FreeItemMemory(i);
CListCtrl::DeleteAllItems();
UnlockWindowUpdate();
return nItemCount;
}
int nDelCount = 0;
LockWindowUpdate();
for (int i = 0; i < nItemCount; i++)
{
if (ExamItemStates(i, dwStates))
{
DeleteItem(i--);
nItemCount--;
nDelCount++;
}
}
UnlockWindowUpdate();
return nDelCount;
}
void CReportCtrl::SortItems(int nColumn, BOOL bAscending)
{
EndEdit(TRUE);
if (nColumn < 0 || nColumn >= GetColumnCount() || !IsSortable())
return;
// do the sorting
m_nSortCol = nColumn;
m_bSortAscending = bAscending;
BOOL bEnd = FALSE;
int nSep1 = -1;
int nSep2 = _FindSeparator(-1, nColumn);
do
{
if (nSep2 < 0)
{
nSep2 = GetItemCount();
bEnd = TRUE;
}
_PartialSort(nSep1 + 1, nSep2 - 1);
nSep1 = _FindSeparator(nSep2 - 1, nColumn);
nSep2 = _FindSeparator(nSep1, nColumn);
} while (!bEnd && nSep1 >= 0);
GetParent()->SendMessage(WM_ITEM_SORTED, (WPARAM)m_nSortCol, (LPARAM)m_bSortAscending);
}
BOOL CReportCtrl::SetItemText(int nItem, int nSubItem, LPCTSTR lpText)
{
EndEdit(TRUE);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -