12.2 wxListCtrl

列表控件使用四种形式中的一种来显示子项:多列视图,多列报告视图,大图标方式以及小图标方式.下图分别对齐进行了演示.每一个子项使用一个长整型的索引来表示的,随着子项的增加,删除以及排序,这个索引可能发生变化.和树状控件不同,列表框默认采用允许多选的方式,不过你还是可以在创建窗口的时候通过类型指定只允许单选.如果你需要对所有子项进行排序,你可以提供一个排序函数.在多列报告视图中,可以给每一列增加一个标题,点过拦截标题单击事件可以实现一些附加操作比如按当前列进行排序.每一列的宽度既可以通过代码改变,也可以通过用户使用鼠标拖拽来改变.

列表控件的每个子项同样可以绑定一些客户区数据,但是和树状控件不同,每一个列表框的子项只能绑定一个长整型数据.如果你希望给每一个子项绑定一个特定对象,你需要自己实现一个从长整型到特定数据对象之间的隐射,并且自己负责那些数据对象的创建和释放.

wxListCtrl的窗口类型

wxLC_LIST 使用可选的小图标进行多列显示.列数是自动计算的,不需要设置象wxLC_REPORT那样设置列数,换句话说,这只是一个自动换行的排列.
wxLC_REPORT 单列或者多列报告方式,并且可以设置可选的标题.
wxLC_VIRTUAL 指定显示的文本由应用程序动态提供; 只能用于wxLC_REPORT方式.
wxLC_ICON 大图标方式显示,可选显示文本标签.
wxLC_SMALL_ICON 小图标方式显示,可选显示文本标签
wxLC_ALIGN_TOP 图标顶端对齐. 仅适用于Windows.
wxLC_ALIGN_LEFT 图标左对齐.
wxLC_AUTO_ARRANGE 图标自动排列.仅适用于Windows.
wxLC_EDIT_LABELS 标签可编辑; 当编辑动作开始时应用程序将收到通知.
wxLC_NO_HEADER 在报告模式下不显示标题.
wxLC_SINGLE_SEL 指定单选模式; 默认为多选模式.
wxLC_SORT_ASCENDING 从小到大排序. 应用程序需要在SortItems中提供自己的排序函数.
wxLC_SORT_DESCENDING 从大到小排序. 应用程序需要在SortItems中提供自己的排序函数.
wxLC_HRULES 在报告模式中显示每行之间的标尺.
wxLC_VRULES 在报告模式中显示每列之间的标尺.

wxListCtrl事件

wxListCtrl产生wxListEvent类型的事件,如下表所示,事件可以父子窗口之间传递,wxListEvent::GetIndex可以用来返回针对单个子项的事件中的子项索引.

EVT_LIST_BEGIN_DRAG(id, func) EVT_LIST_BEGIN_RDRAG(id, func) 这个事件在拖放开始的时候产生,如果要实现拖放,你需要提供拖放操作的剩下的部分.通过wxListEvent::GetPoint函数获取当前的鼠标位置.
EVT_LIST_BEGIN_LABEL_EDIT(id, func) EVT_LIST_END_LABEL_EDIT(id, func) 用户正准备编辑标签或者结束编辑的时候产生,使用Veto函数可禁止这种编辑行为.wxListEvent::GetText则返回当前的标签.
EVT_LIST_DELETE_ITEM(id, func) EVT_LIST_DELETE_ALL_ITEMS(id, func) 事件在某个子项被删除或者所有的子项都被删除的时候产生.
EVT_LIST_ITEM_SELECTED(id, func) EVT_LIST_ITEM_DESELECTED(id, func) 在子项的选中状态发生改变的时候产生.
EVT_LIST_ITEM_ACTIVATED(id, func) 在某个子项被激活(鼠标双击或者通过键盘)的时候产生.
EVT_LIST_ITEM_FOCUSED(id, func) 当当前焦点子项发生改变的时候产生.
EVT_LIST_ITEM_MIDDLE_CLICK(id, func) EVT_LIST_ITEM_RIGHT_CLICK(id, func) 在子项被鼠标以中键或者右键单击的时候产生.
EVT_LIST_KEY_DOWN(id, func) 在有针对列表控件的按键事件的时候产生. 使用wxListEvent::GetKeyCode函数来得到当前按键的编码.
EVT_LIST_INSERT_ITEM(id, func) 新的子项插入的时候产生.
EVT_LIST_COL_CLICK(id, func) EVT_LIST_COL_RIGHT_CLICK(id, func) 某一列被单击的时候产生.使用wxListEvent::GetColumn函数获得被单击的列索引.
EVT_LIST_COL_BEGIN_DRAG(id, func) EVT_LIST_COL_DRAGGING(id, func) EVT_LIST_COL_END_DRAG(id, func) 列大小发生改变的时候或者结束改变的时候产生.你可以使用Veto函数来禁止这种改变.使用wxListEvent::GetColumn获得关联的列索引.
EVT_LIST_CACHE_HINT(id, func) 如果你正在实现一个续列表控件,你可能想在某一组子项被显示之前更新其内部数据.这个事件通知你进行这个操作的最合适的时机.使用wxListEvent::GetCacheFrom函数和 wxListEvent::GetCacheTo函数来获得即将更新的子项的索引范围.

wxListItem

你需要使用wxListItem这个类在列表控件中进行插入,设置子项属性或者获取子项属性的操作.

SetMask函数用来指示你希望使用的列表子项属性,如下表所示:

wxLIST_MASK_STATE state属性有效.
wxLIST_MASK_TEXT text属性有效.
wxLIST_MASK_IMAGE image属性有效.
wxLIST_MASK_DATA data属性有效.
wxLIST_MASK_WIDTH width属性有效.
wxLIST_MASK_FORMAT format属性有效.

使用SetId函数设置子项基于0的索引值,使用SetColumn函数设置当控件在报告模式时基于0的列索引.

SetState函数则用来设置下表所示的子项状态:

wxLIST_STATE_DONTCARE 不关心子项状态.
wxLIST_STATE_DROPHILITED 子项正被高亮显示以便接受一个拖放中的放置事件(仅适用于Windows).
wxLIST_STATE_FOCUSED 子项正拥有焦点.
wxLIST_STATE_SELECTED 子项正被显示.
wxLIST_STATE_CUT 子项已被剪切(仅适用于Windows).

SetStateMask则用来设置当前正在更改的状态,参数和上表中的值相同.

SetText函数用来设置标签或者标题文本.SetImage函数用来设置子项的图片在图片列表中的索引.

SetData函数则用来设置和子项绑定的长整型客户区数据.

SetFormat函数用来设置列表模式时的对齐模式,其值为wxLIST_FORMAT_LEFT, wxLIST_FORMAT_RIGHT或wxLIST_FORMAT_CENTRE(或者wxLIST_FORMAT_CENTER), SetColumnWidth函数则可以用来设置列宽.

还有一些其它的可视属性可以通过下面的这些函数设置:SetAlign, SetBackgroundColour, SetTextColour和SetFont.这些属性不需要设置掩码标记.并且所有Set函数都拥有一个对应的Get函数来获取相应的设置.

下面的代码用wxListItem来选择第二个子项,并且设置其文本标签以及字体颜色.

wxListItem item;
item.SetId(1);
item.SetMask(wxLIST_MASK_STATE|wxLIST_MASK_TEXT);
item.SetStateMask(wxLIST_STATE_SELECTED);
item.SetState(wxLIST_STATE_SELECTED);
item.SetTextColour(*wxRED);
item.SetText(wxT("Red thing"));
listCtrl->SetItem(item);

你也可以直接通过另外的函数来设置这些属性.比如SetItemText, SetItemImage, SetItemState, GetItemText, GetItemImage, GetItemState等等,我们马上就会谈到这些函数.

wxListCtrl成员函数

在windows平台上可以调用Arrange函数在大图标或者小图标的方式下进行排列图标.

AssignImageList给列表控件绑定一个图标列表,图形列表的释放由列表控件负责,SetImageList也可以实现同样的功能,不过图片列表的释放由应用程序自己负责,使用wxIMAGE_LIST_NORMAL或wxIMAGE_LIST_SMALL来指定用于哪种显示形式,GetImageList则用来获取对应的图片列表指针.

InsertItem函数用来在控件的特定位置中插入一个子项.可以传递的参数包括几种形式:直接传递已经设置了成员变量的 wxListItem对象.或者一个索引和一个字符串,一个索引和一个图片索引以及一个索引,一个标签和一个图片索引等. InsertColumn函数则用来在报告视图中插入一列.

ClearAll函数删除所有的子项以及报告视图中的列信息,并产生一个所有子项被删除的事件.DeleteAllItems删除所有的子项并产生所有的子项被删除的事件.DeleteItem删除某一个子项并产生子项被删除事件.DeleteColumn则用来删除报告视图中的某一列.

SetItem函数通过传递一个wxListItem变量来设置某个子项的属性,就象前面例子中的那样,或者你还可以传递一个索引,一个列,一个标签以及图片索引参数.GetItem则用来查询那些经由wxListItem::SetId函数设置的子项索引的信息.

SetItemData函数用来给某个子项绑定一个长整数.在绝大多数的平台上,都可以将一个指针强制转换成长整数,以便这里可以设置一个指针,但是某些平台上,这样作是不适合的,这时候你可以通过一个哈希映射来将一个长整数和某个对象对应.GetItemData则返回某个子项绑定的长整数.需要注意的是,子项的位置随着其它子项的插入或者删除以及排序操作,将会发生改变,因此不适合用来索引一个子项,但是子项绑定的客户数据是不会随子项位置的变化而变化的,可以用来唯一标识一个子项.

SetItemImage函数采用子项索引和图片索引两个参数给某个子项指定一个图标.

SetItemState用来设置子项的某个状态值,必须通过一个掩码来指定哪个状态的值将改变,参考前面介绍wxListItem的部分.GetItemState则用来返回某个给定子项的状态.

SetItemText函数和GetItemText函数用来设置和获取某个子项的文本标签.

SetTextColour用来设置所有子项的文本标签的颜色,SetBackgroundColour则用来设置控件的背景颜色. SetItemTextColour和SetItemBackgroundColour用来设置在报告模式下某个单独子项的文本前景色和背景色.以上函数都由对应的Get函数.

使用EditLabel函数开始编辑某个子项的标签,这将会导致一个wxEVT_LIST_BEGIN_LABEL_EDIT事件.你可以通过这个事件的GetEditControl函数获得对应编辑控件的指针(仅适用于windows).

EnsureVisible将使得指定的子项处于可见区域.ScrollList用来在某个方向上滚动指定的象素(仅适用于 windows),RefreshItem用来刷新某个子项的显示,RefreshItems则用来刷新一系列子项的显示,这两个函数主要用在使用虚列表控件而子项的内部数据已经改变时.GetTopItem用于在列表或者报告视图中返回第一个可见的子项.

FindItem可以被用来查找指定标签,客户区数据或者位置的子项.GetNextItem则用来查找某些指定状态的子项(比如所有正被选中的子项).HitTest函数用来返回给定位置的子项.GetItemPosition返回大图标或者小图标视图中某个子项的相对于客户区的起始座标.GetItemRect则返回相对于客户区的座标以及子项所占的大小.

你可以动态改变列表控件的显示类型而不需要先释放再重新创建它:使用SetSingleStyle函数指定一个类型,比如wxLC_REPORT.指定false参数来移除某一个显示类型.

在报告模式中使用SetColumn函数来设置某列的信息,比如标题或者宽度,参考前面的wxListItem的相关介绍,作为一个简单的替代版本,SetColumnWidth用来直接设置某个列的宽度.使用GetColumn和GetColumnWidth来获取对应的设置.而 GetColumnCount则用来获取当前的列数.

GetItemCount函数用来返回列表控件中子项的数目,GetSelectedItemCount函数则用来返回选中的子项的数目.GetCountPerPage函数在大小图标方式中返回所有子项的数目,而在列表和报告模式中返回可见区域可以容纳的子项的数目.

最后,SortItems函数可以被用来对列表控件中的子项进行排序.这个函数的参数为比较函数wxListCtrlCompare的地址,这个比较函数使用的参数包括两个子项对应的客户区数据,另外一个更深入的整数,然会另外一个整数,如果两个子项比较的结果为相等,则返回0,前一个大于后一个则返回正数,前一个小于后一个则返回负数.当然,为了排序功能更好的工作,你需要给每个子项指定一个长整型的客户区数据(可以通过 wxListItem::SetData函数).这些客户区数据被传递给比较函数以实现比较.

使用wxListCtrl

下面的代码创建和显示了一个报告模式的列表控件,这个列表控件公有三列10个子项,每一行开始的地方都会显示一个16x16的文件图标:

#include "wx/listctrl.h"
// 报告模式中的图片
#include "file.xpm"
#include "folder.xpm"
// 创建一个报告模式的列表控件
wxListCtrl* listCtrlReport = new wxListCtrl(
    this, wxID_ANY, wxDefaultPosition, wxSize(400, 400),
    wxLC_REPORT|wxLC_SINGLE_SEL);
// 绑定一个图片列表
wxImageList* imageList = new wxImageList(16, 16);
imageList->Add(wxIcon(folder_xpm));
imageList->Add(wxIcon(file_xpm));
listCtrlReport->AssignImageList(imageList, wxIMAGE_LIST_SMALL);
// 插入三列
wxListItem itemCol;
itemCol.SetText(wxT("Column 1"));
itemCol.SetImage(-1);
listCtrlReport->InsertColumn(0, itemCol);
listCtrlReport->SetColumnWidth(0, wxLIST_AUTOSIZE );
itemCol.SetText(wxT("Column 2"));
itemCol.SetAlign(wxLIST_FORMAT_CENTRE);
listCtrlReport->InsertColumn(1, itemCol);
listCtrlReport->SetColumnWidth(1, wxLIST_AUTOSIZE );
itemCol.SetText(wxT("Column 3"));
itemCol.SetAlign(wxLIST_FORMAT_RIGHT);
listCtrlReport->InsertColumn(2, itemCol);
listCtrlReport->SetColumnWidth(2, wxLIST_AUTOSIZE );
// 插入10个子项
for ( int i = 0; i < 10; i++ )
{
    int imageIndex = 0;
    wxString buf;
    // 插入一个子项,字符串用于第1栏,
    // 图片索引为0
    buf.Printf(wxT("This is item %d"), i);
    listCtrlReport->InsertItem(i, buf, imageIndex);
    // 子项可能由于各种原因(比如:排序)改变索引,
    // 因此将现在的索引保存为客户区数据
    listCtrlReport->SetItemData(i, i);
    // 为第2栏设置一个文本
    buf.Printf(wxT("Col 1, item %d"), i);
    listCtrlReport->SetItem(i, 1, buf);
    // 为第三栏设置一个文本
    buf.Printf(wxT("Item %d in column 2"), i);
    listCtrlReport->SetItem(i, 2, buf);
}

虚列表控件

通常,列表控件自己保存所有子项相关的文本标签,图片以及其它可见属性的信息,对于小量数据来说,这是不成问题的,但是如果你有成千上万的子项,你可能需要实现一个虚的列表控件. 虚列表控件由应用程序保存子项的数据,你需要重载它的三个虚函数OnGetItemLabel,OnGetItemImage和 OnGetItemAttr,列表控件会在需要的时候调用它们.你必须调用SetItemCount函数来设置当前的子项个数,因为你将不会实际增加任何子项.你还可以处理EVT_LIST_CACHE_HINT事件以便在某一部分子项即将被显示直接更新相关的内部数据,下面是重载三个函数的一个简单的例子:

wxString MyListCtrl::OnGetItemText(long item, long column) const
{
    return wxString::Format(wxT("Column %ld of item %ld"), column, item);
}
int MyListCtrl::OnGetItemImage(long WXUNUSED(item)) const
{
    // 全部的子项都返回0号索引的图片
    return 0;
}
wxListItemAttr *MyListCtrl::OnGetItemAttr(long item) const
{
    // 这个成员是内部使用用来为每一个子项保存相关属性信息
    return item % 2 ? NULL : (wxListItemAttr *)&m_attr;
}

下面演示怎样创建一个虚列表控件,我们不用增加任何子项,并且故意将它的子项个数设置为一个略显夸张的值:

virtualListCtrl = new MyListCtrl(parent, wxID_ANY,
      wxDefaultPosition, wxDefaultSize, wxLC_REPORT|wxLC_VIRTUAL);
virtualListCtrl->SetImageList(imageListSmall, wxIMAGE_LIST_SMALL);
virtualListCtrl->InsertColumn(0, wxT("First Column"));
virtualListCtrl->InsertColumn(1, wxT("Second Column"));
virtualListCtrl->SetColumnWidth(0, 150);
virtualListCtrl->SetColumnWidth(1, 150);
virtualListCtrl->SetItemCount(1000000);

当控件内部数据改变时,如果子项总数改变,你需要重新设置其数目,然后调用wxListCtrl::RefreshItem或者wxListCtrl::RefreshItems来刷新子项的显示.

参考samples/listctrl目录中的完整的例子.