翻译来源:https://www.codeproject.com/Articles/15480/Multi-line-List-Control
作者:Dave Calkins
- 下载demo用VS2015打开需进行以下改动:
- MultilineList.cpp增加int x1,
- stdafx.h 只保留
-
#include <afxwin.h> // MFC core and standard components #include <afxext.h> // MFC extensions #include <afxdisp.h> // MFC Automation classes #include <afxdtctl.h> // MFC support for Internet Explorer 4 Common Controls #include <afxcmn.h> // MFC support for Windows Common Controls
- 下载演示项目 - 176 Kb
- 下载源 - 11.3 Kb
介绍
本文提供了一个自定义的控制,显示多列列表项,与单个单元多行文本支持。单元格文本可以包含嵌入的'\ n'字符,这将强制换行符。此外,根据需要执行单词换行以将文本保留在指定的列宽中。
其实用性是它允许您显示可以包含多行文本的项目列表。您和/或用户设置列宽,并且控件动态地选择行高以支持单元格文本。标准列表 控件不支持多行文本(或支持多行文本所需的不同行高)。
上面提供的演示项目提供了源代码和可执行程序,用于填充控件,如上图所示。演示应用程序还通过更新标题栏来处理选择更改的通知,以指示已选择哪个项目。
该代码是使用Visual Studio .NET 2003(SP1)进行编译的(Visual C ++ v7.1)。
背景
通常在报告模式下使用标准列表 控件来满足以表格格式显示信息的需要。该解决方案的局限性在于,单元格中不支持多行文本。
这种习俗控制与解决这一限制的目的创建的。这个想法是允许控制用户设置列宽,并使控件根据需要动态调整单独的行高度,以适应这些宽度内的文本。该控制应根据需要自动换行的文本,并且应该通过强制换行符兑现任何嵌入“\ n”字符。
在设计和编码此控件时,重点仅在于消除上述限制,而不是创建最终的网格控制(参见“部分完整的网格控制文章”)。因此,这种控制并不能完成标准列表 控制的所有操作,远远低于许多高端网格控制。这个想法是以表格格式提供文本多列数据,以便多行文本将按预期工作。有限的范围旨在为标准列表 控制的特定限制提供一个非常简单,易于使用的解决方案。
话虽如此,这种控制不是从标准列表 控制中得出的,因为修改现有控件以消除单行限制似乎不可行。因此,该接口不是标准列表 控件的替代。有关如何使用控件的详细信息,请参阅文章的其余部分。
使用代码
将控件添加到您的项目
要在代码中使用控件,请将CMultiline List .h和CMultiline List .cpp文件添加到您的项目中。
创建控件
有关创建和使用自定义的背景控制 ,你不妨读一读克里斯·莫德的创建自定义控制 [ ^ ]文章。
如果你正在使用Visual Studio对话框编辑器,你可以创建自定义控制,并设置Class
在属性编辑器中的字符串。在自定义,您可以右键单击控制,并选择“添加变量”,并添加一个控制型的变量。CMultilineList
CMultilineList
如果你想自己动态创建控件,只需创建一个类的实例,然后调用该方法。CMultilineList
Create
填写列表
您首先要做的是设置列表的总体大小(#列和#行)。完成此操作后,您可以设置列宽,指定列标题的文本,然后使用文本填充单元格。用于执行这些任务的公共方法(和其他几个)如下所示。
行和列索引为零,并且与标准列表 控件不存在项目/子项目的区别(因为只提供报表/网格样式视图)。
/** * Sets the overall geometry of the grid. When reducing the size using this * method, anything which is then outside the new size is deleted. You must call * this before anything else to ensure the size you need is available. * @param nCols Desired number of columns (which are numbered [0...N-1]) * @param nRows Desired number of rows (which are numbered [0...N-1]) */ void SetSize(int nCols, int nRows); /** * Gets the current overall geometry of the grid. * @param nCols will be initialized with the number * of columns (which are numbered [0...N-1]) * @param nRows will be initialized with * the number of rows (which are numbered [0...N-1]) */ void GetSize(int & nCols, int & nRows); /** * Sets the text shown in the heading above the specified column. * @param col the column number [0...N-1] * @param heading the text to be displayed */ void SetColHeading(int col, LPCTSTR heading); /** * Sets the width of the specified column in pixels. Note that the user cal * also change this themselves by dragging the column separators in the * header control. * @param col the column number [0...N-1] * @param width desired width of the column in pixels */ void SetColWidth(int col, int width); /** * Sets the text for the specified cell. The text may contain '\n' which * will cause line breaks and heightening of the row as needed. The text * will also be word-wrapped to ensure it fits within the column width. * @param col the column number [0...N-1] * @param row the row number [0...N-1] * @param text the desired text for the cell */ void SetCellText(int col, int row, LPCTSTR text); /** * Gets the current text in the specified cell. * @param col the column number [0...N-1] * @param row the row number [0...N-1] * @return text in the specified cell */ CString GetCellText(int col, int row); /** * changes the current selection to the specified row. changing the selection * programmatically using this method will not trigger a WM_COMMAND notification * message to be sent to the parent window. * @param row the row number to select [0...N-1] */ void SetSelRow(int row); /** * Gets the row number [0...N-1] of the currently selected row. * @return the currently selected row number * [0...N-1] or -1 if no row is currently selected */ int GetSelRow(); /** * Gets the row which is displayed at the specified client co-ords in the window * @param pt the point in client coords * @return the row number [0...N-1] which the specified * point is over (or -1 if there is no row at that point) */ int GetRowFromPoint(CPoint pt); /** * Ensure the specified row is visible * @param row the row number [0...N-1] */ void EnsureRowIsVisible(int row);
使用上述方法的一个简单例子如下所示。在下面的例子中,m_items
是一个类型的变量。CMultilineList
// create a list with 3 columns and 5 rows m_items.SetSize(3,5); // first two columns 100 pixels wide and the third column 200 pixels wide m_items.SetColWidth(0,100); m_items.SetColWidth(1,100); m_items.SetColWidth(2,200); // set the column heading text m_items.SetColHeading(0,_T("Name")); m_items.SetColHeading(1,_T("Quantity")); m_items.SetColHeading(2,_T("Description")); // populate the list data m_items.SetCellText(0,0,_T("Coffee Beans")); m_items.SetCellText(1,0,_T("12")); m_items.SetCellText(2,0, _T("An essential part of the daily diet for ensuring " ) _T("productivity is at required levels.\n\nNOTE: Decaf is for wimps!")); m_items.SetCellText(0,2,_T("Water")); m_items.SetCellText(1,2,_T("10")); m_items.SetCellText(2,2,_T("Listed as a dependency of the coffee beans module."));
上述示例代码的结果如下图所示:
处理列表中的通知
当用户单击控件以选择项目时,会将通知发送到控件的父窗口。为了简单起见,使用列表框LBN_SELCHANGE
消息(而不是使用列表 控制通知消息)。这是作为WM_COMMAND
消息发送到父,就像列表框一样。以下代码片段摘自演示应用程序,并通过更新标题栏文本来说明响应选择更改。
BOOL CMultilineListDemoDlg::OnCommand(WPARAM wParam, LPARAM lParam) { // IDC_ITEMS is the ID assigned to the control in this dialog if ((HIWORD(wParam) == LBN_SELCHANGE) && (LOWORD(wParam) == IDC_ITEMS)) { int selRow = m_items.GetSelRow(); if (selRow < 0) { SetWindowText(_T("MultilineListDemo")); } else { CString s; s.Format(_T("MultilineListDemo (you have selected %s)"), m_items.GetCellText(0,selRow)); SetWindowText(s); } } return CDialog::OnCommand(wParam, lParam); }
兴趣点
虽然目标是增加多行文本,创建自定义的控制做,在一堆其他需求带来的。有一些被认为是理所当然的事情的标准控制,必须在自定义处理小号控制。例如,维护当前字体,找出如何对所有内容进行缩放/定位,并适当地响应事件,以使控件的外观和行为类似于“正常” 列表 控件,以及在初始测试期间弹出的其他鬼祟的东西。
支持多行文本的主要影响之一是不同的行高。当呈现列表的可见区域时,当然有必要计算这些行的行高。但是,当例如使滚动条按预期工作并支持项目选择时,需要知道所有的行高。该类使用std::map
将整数行数映射到像素行高的映射。假设地图中的任何高度都是有效的。通过从地图中删除行高度而使其无效。所以,例如,当单元格的文本被改变时,相应的行高度被无效。当列宽被用户拖动(或以编程方式设置)时,所有行高都被无效。CalculateRowHeights()
然后,该方法用于计算地图中尚未存在的任何行高。代码的许多其他部分然后依赖于能够拥有这个行高的映射来进行计算。
最后,免责声明可能是必要的。我确信我错过了标准控制的一些“标准”行为,尽管希望我不会错过所有的行为。没有完全支持样式和繁重的定制等,还没有提供全套通知。所以,这是一个初始/草稿发布。
参考
- “创建自定义控制的”,由克里斯·莫德 [ ^ ] -提供了有关创建自己的自定义背景控制和利用别人的自定义控制在你的代码。
- “改变所有者绘制控件中的行高”,由Uwe Keim [ ^ ] - 说明列表视图控件派生类的行列高度变化,但具有统一的行高。
- “电网控制 ”,由Chris Maunder,Ken Bertelson,Mario Zucca和Fred Ackers [ ^ ] - 功能完备的网格控制 ; 比我想要做的更多,多行单元格文本似乎不是主要功能。网格控制评论部分解释了如何进行多行文本,但它似乎比我想要的更多。我的目标是能够设置文本,让控件为我量身定做。
- Andre Arpin [ ^ ] - “支持类别”的报告控制 -支持多行文本,但它也比我想做的更多,文档数量有限,界面似乎更多。我正在寻找一个非常简单的界面来处理多行文本,而无需其他所有的响铃和口哨。
- “CodeProject Article Writer Helper”,由Jason Henderson撰写 [ ^ ] - 编写CodeProject文章的好工具。