65.9K
CodeProject 正在变化。 阅读更多。
Home

另一个报表列表控件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.89/5 (90投票s)

2003年12月2日

6分钟阅读

viewsIcon

1333412

downloadIcon

17499

一个支持排序、子项编辑、子项图片、子项颜色等的报表样式 CListCtrl。

引言

通过观察“报表列表控件”在各种应用程序中的广泛应用,实在无需介绍列表控件是什么以及报表样式是什么,所以我将跳过“它是什么”而直接进入“它做什么”。

特点

项目排序

当用户左键单击列标题,或开发人员调用 CReportCtrl::SortItems 时,整列项目将被排序。它们的文本格式将被适当识别(十进制数字、十六进制数字、小数点、百分比、日期和时间、纯文本)并相应排序。

排序完成后,会向父窗口发送 WM_ITEM_SORTED 消息,列索引作为 wParam 传递,排序方法作为 lParam 传递(0=降序,1=升序)。

相关方法
BOOL IsSortable() const; // Is sort allowed?
BOOL SetSortable(BOOL bSet); // Allow/disallow sorting
BOOL IsSortAscending() const;
int GetSortedColumn() const;
// Sort a specified column.
void SortItems(int nColumn, BOOL bAscending); 

项目排序分隔符

有时您可能不希望整列一起排序,而是希望它们像多个“部分”一样,每个项目“部分”在内部排序,而“部分”本身保持不变。请想象一下这种场景,您正在使用该控件显示一个审计报表表格,所有行都是数值,最后一行是所有上述值的总和;现在,如果用户按降序排序,包含总和值的行将移到顶部,因为基本上总和值很可能大于任何其他值。这并不是您想要的,您希望总和行始终停留在表格底部,对吗?现在您希望有一种方法可以在总和行和所有其他数据行之间插入某种“分隔符”,这样排序就不会影响总和行。如何做到这一点?很简单,使用 CReportCtrl::SetSortSeparator

下面是一个未排序的表格,我希望这3个部分(蓝色、绿色、黄色)单独排序。

unsorted

不使用任何分隔符进行排序

m_wndList.SetSortSeparator(NULL); // Disable sort-separator
// Sort the first column in ascending order
m_wndList.SortItems(0, TRUE);

sorted without using separator

看看表格是如何变得一团糟,3个部分被拆散了,这不是我们想要的。但是如果在上面的步骤中,你使用了排序分隔符

// m_wndList.SetSortSeparator(NULL); // Disable sort-separator
// m_wndList.SortItems(0, TRUE); // Sort the first column in ascending order

// Use string "Separator" as the sort-separator
m_wndList.SetSortSeparator(_T("Separator")); 
// Sort the first column in ascending order
m_wndList.SortItems(0, TRUE);

Sample screenshot

现在好了,第三张图片正是我们所期望的。

相关方法
void SetSortSeparator(LPCTSTR lpSortSeparator);
LPCTSTR GetSortSeparator() const;

子项文本编辑

当用户双击网格,或者左键单击之前有选中标记和焦点的网格,或者开发者调用 CReportCtrl::StartEdit 时,该子项的文本变得可编辑。

当项目文本正在编辑时,如果用户在控件中点击正在编辑的网格以外的任何地方,或者按下“Esc”键,或者按下“Enter”键,或者开发人员调用 CReportCtrl::EndEdit,项目编辑将结束,并且所做的更改将根据结束编辑的操作类型进行提交或取消。

相关方法
BOOL IsEditable() const; // Is Item text editable?
void SetEditable(BOOL bSet = TRUE); // Allow item text editting

// Display the editbox, previous edit are committed
BOOL StartEdit(int nItem, int nSubItem); 

BOOL EndEdit(BOOL bCommit = TRUE); // Commit/cancel text edit, hide the editbox
CEdit* GetEditControl();

自定义复选框样式

报表列表控件中的复选框很有用,但我经常发现我确实需要对它们进行更多的控制,例如,有时我希望在用户单击复选框时收到通知,或者我希望将复选框设置为只读,以便用户无法通过输入更改复选框的选中/未选中值。在普通的 MFC CListCtrl 中实现这些可能相当棘手,但在此类中却变得轻而易举。

通过调用 CReportCtrl::SetCheckboxeStyle 可以指定复选框样式。可识别的样式有

  • RC_CHKBOX_NONE -- 不显示复选框
  • RC_CHKBOX_NORMAL -- 普通样式,允许多个选中标记
  • RC_CHKBOX_SINGLE -- 仅允许单个选中。复选框是互斥的,就像使用单选按钮一样。
  • RC_CHKBOX_DISABLED -- 禁用,用户输入无法选中/取消选中。

当用户单击列表项的复选框时,将向父窗口发送 WM_ON_CHKBOX 消息。项目索引作为 wParam 传递,鼠标事件(如 WM_LBUTTONDOWNWM_RBUTTONDOWN 等)作为 lParam 传递。

相关方法
void SetCheckboxeStyle(int nStyle = RC_CHKBOX_NORMAL); // Set checkbox styles.
int GetCheckboxStyle() const;
// Example:
 
// Make the checkbox read-only
m_wndList.SetCheckboxeStyle(RC_CHKBOX_DISABLED);

子项图像和子项颜色

每个网格都可以有自己的图像、文本颜色和背景颜色。要使用图像,您需要首先调用 CReportCtrl::SetImageList 来指定图像列表或位图资源ID。

相关方法
// Column header images
BOOL SetHeaderImage(int nColumn, int nImageIndex, BOOL bLeftSide = TRUE);
int GetHeaderImage(int nColumn) const;
CImageList* SetHeaderImageList(UINT nBitmapID, COLORREF crMask = RGB(255, 0, 255));
CImageList* SetHeaderImageList(CImageList* pImageList);
// Sub-item images
BOOL SetItemImage(int nItem, int nSubItem, int nImageIndex);
int GetItemImage(int nItem, int nSubItem) const;
CImageList* SetImageList(UINT nBitmapID, COLORREF crMask = RGB(255, 0, 255));
CImageList* SetImageList(CImageList* pImageList);
CImageList* GetImageList() const;
// Sub-item Text & Background Color
void SetItemTextColor(int nItem = -1, int nSubItem = -1, 
  COLORREF color = COLOR_INVALID, BOOL bRedraw = TRUE);
COLORREF GetItemTextColor(int nItem, int nSubItem) const;
void SetItemBkColor(int nItem = -1, int nSubItem = -1, 
  COLORREF color = COLOR_INVALID, BOOL bRedraw = TRUE);
COLORREF GetItemBkColor(int nItem, int nSubItem) const;
// Example: Set image and color for grid - 1st row 3rd column

m_wndList.SetImageList(IDB_BITMAP1); // Using bitmap resource "IDB_BITMAP1"
m_wndList.SetItemImage(0, 2, 0); // Set the 1st row 3rd column with image index 0

// Set grid colors
m_wndList.SetItemTextColor(0, 2, RGB(255, 0, 0), TRUE); // Grid text color: red
m_wndList.SetItemBkColor(0, 2, RGB(0, 255, 0), TRUE); // Grid bkground color: green

方便的项目操作

列表项操作变得非常简单直观,您可以选择/取消选择、选中/取消选中、删除与给定状态或给定状态组合匹配的项。每个列表项可以具有以下一个或多个状态

  • RC_ITEM_ALL - 所有项目,无论状态如何
  • RC_ITEM_SELECTED - 选定项目
  • RC_ITEM_UNSELECTED - 未选定项目
  • RC_ITEM_CHECKED - 选中项目
  • RC_ITEM_UNCHECKED - 未选中项目
  • RC_ITEM_FOCUSED - 获得焦点的项目
  • RC_ITEM_UNFOCUSED - 未获得焦点的项目

多个状态可以使用“|”运算符组合。请注意,在任何状态组合中,如果存在 RC_ITEM_ALL,则所有其他状态都将被忽略,并且状态检查将始终返回 TRUE

相关方法
int GetFirstItem(DWORD dwStates = RC_ITEM_ALL, int nStartAfter = -1) const;
int GetLastItem(DWORD dwStates = RC_ITEM_ALL, int nStartBefore = -1) const;
int GetItemCount(DWORD dwStates = RC_ITEM_ALL) const; 
DWORD GetItemStates(int nItem) const;
BOOL ExamItemStates(int nItem, DWORD dwStates) const;
BOOL SetItemStates(int nItem, DWORD dwNewStates);
int SetAllItemStates(DWORD dwOldStates, DWORD dwNewStates);
void InvertItems(int nType); // RC_INVERT_SELECTION or RC_INVERT_CHECKMARK
// Example:

// Select all items which were unselected and unchecked
m_wndList.SetAllItemStates(RC_ITEM_UNSELECTED | RC_ITEM_UNCHECKED, RC_ITEM_SELECTED);

// How many items are not checked?
int n = m_wndList.GetItemCount(RC_ITEM_UNCHECKED);

//Unselect and check all items regardless of their previous states
m_wndList.SetAllItemStates(RC_ITEM_ALL, RC_ITEM_UNSELECTED | RC_ITEM_CHECKED);

// Delete all items which were selected and checked
m_wndList.DeleteAllItems(RC_ITEM_SELECTED | RC_ITEM_CHECKED);

方便的项目定位

此类中广泛实现了项目位置操作,您可以轻松地向上或向下移动项目,或移动到特定位置,或交换两个现有项目等。以下成员函数专为此类操作而设计

int MoveUp(int nItem, int nCount = 1); // Move an item upwards by "nCount" positions.

// Move an item downwards by "nCount" positions.
int MoveDown(int nItem, int nCount = 1); 

int MoveToTop(int nItem); // Move an item up to the top.
int MoveToBottom(int nItem); // Move an item down to the bottom.
int MoveTo(int nItem, int nNewPosition); // Move an item to a particular position 
BOOL SwapItems(int nItem1, int nItem2); // Swap two items including all attributes.

项目文本操作

CListCtrl::SetItemTextCListCtrl 最常用的函数之一。然而,它只接受字符串参数。所以如果我们想显示其他数据类型,我们必须首先将它们转换为字符串表示,但在此类中,我们无需执行这样的繁琐工作。

BOOL SetItemText(int nItem, int nSubItem, INT val);
BOOL SetItemText(int nItem, int nSubItem, UINT val);
BOOL SetItemText(int nItem, int nSubItem, LONG val);
BOOL SetItemText(int nItem, int nSubItem, ULONG val);
BOOL SetItemText(int nItem, int nSubItem, TCHAR val);
BOOL SetItemText(int nItem, int nSubItem, DOUBLE val, int nPrecision = -1);
BOOL SetItemText(int nItem, int nSubItem, 
   const COleDateTime& dateTime, 
   DWORD dwFlags = 0);

如何在基于对话框的应用程序中使用

要使用该类,您需要将 ReportCtrl.hReportCtrl.cpp 添加到您的工作区,并在需要的地方包含 ReportCtrl.h。要创建 CReportCtrl 的实例,您可以选择在对话框上绘制一个 CListCtrl 并使用类向导为其指定 CReportCtrl 类型,或者手动声明一个 CReportCtrl 变量并使用 CReportCtrl::Create 在运行时创建控件窗口。

如何在列表视图中使用

我经常听到有人问:“嘿,这个控件在基于对话框的应用程序中工作得很好,但我如何在 CListView 中使用它呢?”嗯,答案是你不能,相反,你应该让你的视图派生自 CFormView,而不是 CListView,然后将一个列表控件放在你的表单视图上,并使该控件派生自 CReportCtrl。然后,在你的视图类的 OnSize 函数中添加一行代码

void CMyView::OnSize(UINT nType, int cx, int cy) 
{
    CFormView::OnSize(nType, cx, cy);

    // TODO: Add your message handler code here
    m_wndList.ResizeToFitParent(); // Add this line!
}

就是这样,每当您的视图调整大小时,列表控件将自动调整自身大小以占据视图的整个客户区,这正是 CListView 的样子。

历史

2003年12月2日

  • 初始发布。

2003年12月3日

  • 修复了 EndEdit 中项目文本未正确提交的错误。
  • 完成了“排序分隔符”功能的实现。
  • 更新了源文件和演示项目文件下载。

2004年1月1日

  • 修复了 SetItemData 中的一个错误。
  • 添加了消息 WM_EDIT_COMMITTED,该消息在项目文本编辑提交时发送到父窗口。
  • 修复了 SetItemText (double 类型) 中的一个错误。
  • 修复了当同一窗口上有多个 CReportCtrl 对象时,项目排序无法正常工作的错误。
© . All rights reserved.