12.1 wxTreeCtrl

树状控件以层的形式展示信息,它的子项可以展开也可以合并.下图演示了wxWidgets的树状控件例子,它正以不同的字体和风格以及颜色进行展示.每一个树状控件的子项都代表一个wxtreeItemId对象,它拥有一个文本标签和一个可选图标,并且文本和图标的内容都可以动态修改.树状控件可以以单选或者多选的形式创建.如果你希望在wxtreeItemId上绑定一些数据,你需要实现自己的wxTreeItemData派生类,然后调用 wxTreeCtrl::SetItemData函数以及wxTreeCtrl::GetItemData函数.这个数据在子项被释放的时候将会被一并释放(delete调用),如果你将其指向你实际的数据,需要注意避免重复释放.

因为应用程序可以检测到树状控件的子项被单击的事件,你可以用这个特点通过更改子项的图片来达到模拟其他的控件的目的.比如说,你可以很容易用树状控件的子项来模拟一个复选框.

下面的代码演示了怎样创建一个树状控件,定义其子项的绑定数据以及图片:

#include "wx/treectrl.h"
// 声明一个代表和子项绑定的数据的类
class MyTreeItemData : public wxTreeItemData
{
public:
    MyTreeItemData(const wxString& desc) : m_desc(desc) { }
    const wxString& GetDesc() const { return m_desc; }
private:
    wxString m_desc;
};
// 子项相关的图片
#include "file.xpm"
#include "folder.xpm"
// 创建一个树状控件
wxTreeCtrl* treeCtrl = new wxTreeCtrl(
    this, wxID_ANY, wxPoint(0, 0), wxSize(400, 400),
    wxTR_HAS_BUTTONS|wxTR_SINGLE);
wxImageList* imageList = new wxImageList(16, 16);
imageList->Add(wxIcon(folder_xpm);
imageList->Add(wxIcon(file_xpm);
treeCtrl->AssignImageList(imageList);
// 根节点使用文件夹图标,而两个字节点使用文件图标
wxTreeItemId rootId = treeCtrl->AddRoot(wxT("Root"), 0, 0,
                          new MyTreeItemData(wxT("Root item")));
wxTreeItemId itemId1 = treeCtrl->AppendItem(rootId,
                          wxT("File 1"), 1, 1,
                          new MyTreeItemData(wxT("File item 1")));
wxTreeItemId itemId2 = treeCtrl->AppendItem(rootId,
                          wxT("File 2"), 1, 1,
                          new MyTreeItemData(wxT("File item 2")));

wxTreeCtrl的窗口类型

wxTreeCtrl有如下表所示的额外的窗口类型:

wxtr_DEFAULT_STYLE 这个值是各个平台上树状控件实现和默认值最接近的值
wxtr_EDIT_LABELS 是否子项文本可编辑
wxtr_NO_BUTTONS 不必显示用于展开或者合并子项的按钮
wxtr_HAS_BUTTONS 显示用于展开或者合并子项的按钮
wxTR_NO_LINES 不必显示用于表示层级关系的垂直虚线
wxtr_FULL_ROW_HIGHLIGHT 当选中某个子项的时候高亮显示整行(在windows平台上,除非设置了wxtr_NO_LINES,否则这个类型将被忽略)
wxtr_LINES_AT_ROOT 不必显示根节点之间的连线.这个类型只有在设置wxtr_HIDE_ROOT 并且没有设置wxtr_NO_LINES 的时候有效
wxtr_HIDE_ROOT 不显示根节点,这将导致第一层的字节点成为一系列根节点
wxtr_ROW_LINES 使用这个类型在已显示的行之间绘制一个高对比的边界
wxTR_HAS_VARIABLE_ROW_HEIGHT 设置这个类型允许各行采用不同的高度,否则各行都将采用和最大的行高同样的高度.这个类仅适用于树状控件的标准实现(而非各个平台的原生实现)
wxtr_SINGLE 单选模式
wxtr_MULTIPLE 多选模式
wxtr_EXTENDED 允许多选非连续的子项(该功能仅是部分实现)

wxTreeCtrl的事件

树状控件产生wxtreeEvent类型的事件,这种事件可以在父子关系的窗口之间传递.

EVT_TREE_BEGIN_DRAG(id, func)EVT_TREE_BEGIN_RDRAG(id, func) 在用户开始拖放操作的时候产生,这个事件的使用细节请参考第11章,"剪贴板和拖放操作"
EVT_TREE_BEGIN_LABEL_EDIT(id, func) EVT_TREE_END_LABEL_EDIT(id, func) 当用户开始编辑或者刚刚完成编辑子项标签的时候产生
EVT_TREE_DELETE_ITEM(id, func) 当某个子项被删除的时候产生
EVT_TREE_GET_INFO(id, func) 当某个子项的数据被请求的时候产生
EVT_TREE_SET_INFO(id, func) 当某个子项的数据被设置的时候产生
EVT_TREE_ITEM_ACTIVATED(id, func) 当某个子项被激活(双击或者使用键盘选择)的时候产生
EVT_TREE_ITEM_COLLAPSED(id, func) 给定的子项已被收缩(合并)的时候产生
EVT_trEE_ITEM_COLLAPSING(id, func) 给定的子项即将收缩(合并)的时候产生,这个事件可以被Veto以阻止收缩.
EVT_TREE_ITEM_EXPANDED(id, func) 给定子项已被展开的时候产生
EVT_TREE_ITEM_EXPANDING(id, func) 给定子项即将展开的时候产生,这个事件可以被Veto以阻止展开
EVT_TREE_SEL_CHANGED(id, func) 选中的子项发生变化以后(新的子项被选中或者旧的选中项不被选中的时候)产生
EVT_TREE_SEL_CHANGING(id, func) 选中的子项即将发生变化的时候产生,该事件可以被Veto以阻止变化产生
EVT_TREE_KEY_DOWN(id, func) 检测针对该树状控件的键盘事件
EVT_TREE_ITEM_GET_TOOLTIP(id, func) 这个事件仅支持windows平台,它使得你可以给某个子项设置单独的工具提示

wxTreeCtrl的成员函数

下面列出了wxTreeCtrl控件的一些重要的成员函数.

使用AddRoot函数增加第一个子项,然后使用AppendItem, InsertItem或PrependItem来增加随后的子项.使用Delete移除一个子项,使用DeleteAllItems删除某个子项所有的子项,或者使用DeleteChildren删除某个子项的所有直接子项.

使用SetItemText设置某个子项的标签,使用SetItemTextColour,SetItemBackgroundColour,SetItemBold和SetItemFont来设置标签的外观.

如果你想给某个子项指定一幅图片,首先需要使用SetImageList函数将某个图片列表和这个树状控件绑定.每个子项可以指定四个状态的图片,分别是wxTReeItemIcon_Normal,wxTReeItemIcon_Selected, wxtreeItemIcon_Expanded和wxTReeItemIcon_SelectedExpanded,你可以使用 SetItemImage函数给每个状态指定一个图片列表中图片索引.如果你只给wxTReeItemIcon_Normal状态指定了一个索引,那么别的状态也将都使用这个图片.

使用Scroll函数以便将某个子项移动到可见区域,使用EnsureVisible使得这个子项在需要的时候展开以便其可以位于可见区域.使用Expand函数展开某个子项,Collapse和CollapseAndReset函数合并某个子项,后者还将移除其所有的子项,如果你正在使用的树状控件有很多子项,你可能希望只增加可见部分的子项以便提高性能.在这种情况下,你可以处理EVT_TREE_ITEM_EXPANDING事件,在需要的时候才增加子项,在收缩的时候则移除所有子项.而且你还需要调用SetItemHasChildren函数以便没有子项的子项也可以显示一个可扩展按钮,即使它真的没有.

使用SelectItem选择或者去选择某个子项.如果是单选类型,你可以使用GetSelection函数得到正被选中的子项,如果当前没有子项被选中,则返回一个未初始化的wxTReeItemId,你可以调用wxTreeItemId::IsOk函数来判断其有效性.而对于多选类型,你可以使用GetSelections函数获取当前选中的子项,你需要传递一个wxArrayTreeItemItemIds类型的引用作为参数. Unselect函数在单选情况下去选中当前的子项,而UnselectAll函数则用在多选情况下去选中所有正被选中的子项,UnselectItem 函数可以用来在多选情况下去选中某一个子项.

遍历某个树状控件的所有子项也有多种方法:你可以先使用GetRootItem函数获得根节点,然后使用GetFirstChild和 GetNextChild遍历所有子项.使用GetNextSibling和GetPrevSibling获取某个子项后一个和前一个兄弟节点.使用 ItemHasChildren函数判断某个子项是否有字节点,使用GetParent函数获取某个子项的父节点.GetCount函数则用来返回树状控件中所有子项的个数,而GetChildrenCount则返回某个子项的字节点的数目.

HitTest函数在实现你自己拖放的时候是很有用的,它使得你可以通过鼠标位置找到这个位置对应的子项以及子项的某个特定部分.具体返回值请参考相关手册中的内容.使用GetBoundingRect函数可以得到某个子项对应的矩形区域.

更多关于树状控件的信息请参考使用手册以及samples/treectrl中的wxTreeCtrl例子.