Files
minigui-docs/programming-guide-zh/MiniGUIProgGuidePart6Chapter13-zh.md
lisimeng123 12ea4e47e4 update
2022-11-18 00:00:48 +08:00

12 KiB
Raw Blame History

31 树型控件

树型控件treeview以树型的方式显示一系列的分层次的项每个项子项可以包括一个或多个子项。每项或子项包括文字标题和可选的图标用户可以通过点击该项来展开或折叠该项中的子项。树型控件比较适合用来表示具有从属关系的对象例如文件、目录结构或者某一机构的组织情况。

使用 CTRL_TREEVIEW 作为控件类名称,可以通过 CreateWindow 函数调用创建树型控件。

我们在创建树型控件之后,可以通过发送相应的消息来添加、删除、设置、获取和查找节点项。

1.1 树型控件风格

树型控件的风格控制控件的外观。你可以在创建控件时指定最初的控件风格,还可以在之后使用 GetWindowStyle 获取其风格和用 SetWindowStyle 设置新的风格。

带有 TVS_WITHICON 风格的树型控件使用图标来显示每项的折叠和展开状态,相应的图标可以在创建节点项时指定。如果没有为某个节点项指定特定的图标的话,树型控件将使用 MiniGUI 配置文件 MiniGUI.cfg 中指定的 “treefold” 和 “treeunfold” 图标。没有 TVS_WITHICON 风格的树型控件使用一个带方框的 “+” 号来表示一个折叠的节点项,用带方框的 “-” 号来表示展开的节点项。

TVS_SORT 风格的树型控件将对节点项进行自动排序。

带有 TVS_NOTIFY 风格的树型控件将在响应用户操作时产生相应的通知消息和通知码。

【注意】在 MiniGUI 3.0 中,TreeView 控件的 TVS_ICONFORSELECT 风格被取消;使用此风格对 TreeView 控件无任何影响。

1.2 树型控件消息

1.2.1 节点项的创建和删除

树型控件由根节点和一系列的子节点构成。我们可以在使用 CreateWindow 函数创建树型控件时,通过该函数的 dwAddData 参数把一个 TVITEMINFO 类型的结构指针传递给树型控件来指定根节点的属性。具体例子可参见 31.4 中的编程实例。TVITEMINFO 结构中包含了(根)节点的属性信息。

typedef struct _TVITEMINFO
{
        /* 该项的文字标题 */
        char *text;
        
        /* 该项的状态标志 */
        DWORD dwFlags;
        
        /* 折叠项的图标句柄 */
        HICON hIconFold;
        /* 展开项的图标句柄 */
        HICON hIconUnfold;
        
        /* 该项的附加数据 */
        DWORD dwAddData;
} TVITEMINFO;

text 为节点的标题如果创建树型控件时不指定根节点的属性根节点的标题将为“root”。

dwFlags 为节点项的状态标志,TVIF_SELECTED 表明该节点是被选择的项,TVIF_FOLD 表明该节点项初始是折叠的。在添加节点时,只有 TVIF_FOLD 标志是可用的。

hIconFoldhIconUnfold 分别是节点折叠和展开时所使用的用户自定义图标句柄。使用这两项只有在树型控件为 TVS_WITHICON 风格时才是有意义的。

TVM_ADDITEMTVM_INSERTITEM)消息往树型控件中插入一个节点项:

TVITEMINFO tvItemInfo;
GHANDLE item;
item = SendMessage (hTrvWnd, TVM_ADDITEM, 0, (LPARAM) &tvItemInfo);

itemSendMessage 函数返回的代表所添加节点的句柄值,之后我们需要使用该句柄来操作该节点项。

TVM_DELTREE 消息删除一个节点及其所有子项(包括子项的子项):

SendMessage (hTrvWnd, TVM_DELTREE, (WPARAM)item, 0);

item 是一个 GHANDLE 类型的句柄值,该值应该是使用 TVM_ADDITEM 消息添加该节点时 SendMessage 函数返回的句柄值。

1.2.2 节点项属性的设置和获取

TVM_GETITEMINFO 消息用来获取某个节点项的属性信息:

TVITEMINFO tvii;
GHANDLE item;
SendMessage (hTrvWnd, TVM_GETITEMINFO, (WPARAM)item, (LPARAM)&tvii);

item 是所要获取信息的节点项的句柄,tvii 结构用来存放所获取的节点项属性信息。使用时我们要注意,tvii 结构中的 text 所指向的字符缓冲区应该足够大。

TVM_SETITEMINFO 消息用来设置某个节点项的属性:

TVITEMINFO tvii;
GHANDLE item;
SendMessage (hTrvWnd, TVM_SETITEMINFO, (WPARAM)item, (LPARAM)&tvii);

item 是所要设置的节点项的句柄,tvii 结构用包含了所要设置的节点项属性信息。

TVM_GETITEMTEXT 消息获取某个节点项的文字标题:

char *buffer;
SendMessage (hTrvWnd, TVM_GETITEMTEXT, (WPARAM)item, (LPARAM)buffer);

buffer 字符缓冲区应该足够大以存放节点项的文字标题。

节点项的文字标题的长度可以用 TVM_GETITEMTEXTLEN 消息来获取:

int len;
len = SendMessage (hTrvWnd, TVM_GETITEMTEXTLEN, (WPARAM)item, 0);

1.2.3 选择和查找节点项

TVM_SETSELITEM 消息用来选择某个节点项:

GHANDLE item;
SendMessage (hTrvWnd, TVM_SETSELITEM, (WPARAM)item, 0);

item 为所要选择的节点项句柄。

TVM_GETSELITEM 消息获取当前被选择的节点项:

GHANDLE item;
item = SendMessage (hTrvWnd, TVM_GETSELITEM, 0, 0);

item 为当前被选择的节点项的句柄。

TVM_GETROOT 消息用来获取树型控件的根节点:

GHANDLE rootItem;
rootItem = SendMessage (hTrvWnd, TVM_GETROOT,  0, 0);

TVM_GETRELATEDITEM 消息用来获取指定节点的相关节点项:

GHANDLE item;
int related;
GHANDLE relItem;
relItem = SendMessage (hTrvWnd, TVM_GETRELATEDITEM,  related, (LPARAM)item);

item 为指定的节点,related 可以是如下值:

  • TVIR_PARENT:获取 item 节点的父节点
  • TVIR_FIRSTCHILD:获取 item 节点的第一个子节点
  • TVIR_NEXTSIBLING:获取 item 节点的下一个兄弟节点
  • TVIR_PREVSIBLING:获取 item 节点的前一个兄弟节点

SendMessage 函数返回所获取的节点项的句柄值。

TVM_SEARCHITEM 消息用来查找某个特定的节点项:

GHANDLE itemRoot;
const char *text;
GHANDLE found;
found = SendMessage (hTrvWnd, TVM_SEARCHITEM, (WPARAM) itemRoot, (LPARAM) text);

itemRoot 所指定的节点树就是查找的范围,text 所指的字符串就是查找的内容。如果查找成功,SendMessage 函数将返回查找到的节点项的句柄。如果失败则返回0。

TVM_FINDCHILD 消息用来查找节点项的特定子节点:

GHANDLE itemParent;
const char *text;
GHANDLE found;
found = SendMessage (hTrvWnd, TVM_FINDCHILD, (WPARAM) itemParent, (LPARAM) text);

itemParent 所指定的节点的子节点是所要查找的节点范围,text 所指的字符串就是查找的内容。如果查找成功,SendMessage 函数将返回查找到的节点项的句柄。如果失败则返回0。

TVM_FINDCHILDTVM_SEARCHITEM 消息的不同之处在于:TVM_FINDCHILD 只在子节点中查找,而 TVM_SEARCHITEM 在整个节点树中查找。

1.2.4 比较和排序

TVS_SORT 风格的树型控件对节点项进行自动排序。应用程序在使用 TVM_ADDITEM 消息添加节点项时,如果控件没有 TVS_SORT 风格,各项按添加的先后顺序排列;如果有 TVS_SORT 风格,各项按字符串比较次序排列。

字符串的排列次序由树型控件的字符串比较函数确定。初始的字符串比较函数为 strncmp,应用程序可以通过 TVM_SETSTRCMPFUNC 消息来设置新的树型控件字符串比较函数:

SendMessage (hTrvWnd, TVM_SETSTRCMPFUNC, 0, (LPARAM)str_cmp);

str_cmpSTRCMP 类型的函数指针:

typedef int (*STRCMP) (const char* s1, const char* s2, size_t n);

该字符串比较函数比较字符串 s1 和 s2 的最多 n 个字符,并根据比较结果返回一个小于 0、等于 0 或大于 0 的整数。

1.3 树型控件的通知码

树型控件在响应用户点击等操作和发生某些状态改变时会产生通知消息,包括:

  • TVN_SELCHANGE:当前选择的节点项发生改变
  • TVN_DBLCLK:用户双击节点项
  • TVN_SETFOCUS:树型控件获得焦点
  • TVN_KILLFOCUS:树型控件失去焦点
  • TVN_CLICKED:用户单击节点项
  • TVN_ENTER:用户按下回车键
  • TVN_FOLDED:节点项被折叠
  • TVN_UNFOLDED:节点项被展开

如果应用程序需要了解树型控件产生的通知码的话,需要使用 SetNotificationCallback 函数注册一个通知消息处理函数,在该函数中对收到的各个通知码进行应用程序所需的处理。

1.4 编程实例

清单 1.1 中的代码演示了树型控件的使用。该程序的完整源代码可见本指南示例程序包 mg-samples 中的 treeview.c 程序。

清单 1.1 树型控件示例程序

#define IDC_TREEVIEW  100

#define CHAPTER_NUM    5

/* 定义树型控件条目使用的文本 */
static const char *chapter[] = 
{
        "第十六章 树型控件",
        "第十七章 列表型控件",
        "第十八章 月历控件",
        "第十九章 旋钮控件",
        "第二十章 酷工具栏控件",
};

/* 定义树型控件条目使用的文本 */
static const char *section[] =
{
        "控件风格",
        "控件消息",
        "控件通知码"
};

static int BookProc(HWND hDlg, int message, WPARAM wParam, LPARAM lParam)
{
        switch (message) {
                case MSG_INITDIALOG:
                {
                        TVITEMINFO tvItemInfo;
                        int item;
                        int i, j;
                        
                        /* 向树型控件中添加条目 */
                        for (i = 0; i < CHAPTER_NUM; i++) {
                                tvItemInfo.text = (char*)chapter[i];
                                item = SendMessage (GetDlgItem(hDlg, IDC_TREEVIEW), TVM_ADDITEM, 
                                0, (LPARAM)&tvItemInfo);
                                /* 向每个条目中添加子条目 */
                                for (j = 0; j < 3; j++) {
                                        tvItemInfo.text = (char*)section[j];
                                        SendMessage (GetDlgItem(hDlg, IDC_TREEVIEW), TVM_ADDITEM, 
                                        item, (LPARAM)&tvItemInfo);
                                }
                        }
                }
                break;
                
                case MSG_CLOSE:
                EndDialog (hDlg, 0);
                return 0;
        }
        
        return DefaultDialogProc (hDlg, message, wParam, lParam);
}

static TVITEMINFO bookInfo =
{
        "MiniGUI编程指南"
};

/* 对话框模板 */
static DLGTEMPLATE DlgBook =
{
        WS_BORDER | WS_CAPTION,
        WS_EX_NONE,
        100, 100, 320, 240,
        #ifdef _LANG_ZHCN
        "目录",
        #else
        "Book Content",
        #endif
        0, 0,
        1, NULL,
        0
};

/* 这个对话框中只有一个控件:树型控件 */
static CTRLDATA CtrlBook[] =
{
        {
                CTRL_TREEVIEW,
                WS_BORDER | WS_CHILD | WS_VISIBLE | 
                WS_VSCROLL | WS_HSCROLL,
                10, 10, 280, 180,
                IDC_TREEVIEW,
                "treeview control",
                (DWORD)&bookInfo
        }
};

书目录的树型显示 图 1.1 书目录的树型显示

treeview.c 程序用树型控件来显示书目录结构。程序在用对话框模版创建对话框时指定树型控件数据结构 CTRLDATA的dwAddData 项为 &bookInfobookInfo 是一个 TVITEMINFO 类型的结构其中给出了节点的标题为“MiniGUI编程指南”因此所创建的树型控件的根节点标题就是“MiniGUI编程指南”。