标题：树型视图控件
树型视图的风格
几种经常使用的风格。 
TVS_HASBUTTONS == 在父项目中显示（+）或（-）。用户可以通过点击该符号来展开或收起该父项目下的子项目。如果想在根目录下也有这个符号必须指定TVS_LINESATROOT风格。
TVS_HASLINES == 在层次中用线条来连接各个项目名称。
TVS_LINESATROOT == 在根目录下的项目也用线连接。如果没有指定TVS_HASLINES风格，该风格也就会被忽略。 像其它的通用控件一样，树型视图用消息来完成通信。父窗口发送一系列的消息给树型视图，而树型视图发送"notification"消息给它的父窗口。在这方面，树型视图和其它的通用控制没什么两样。
当有事件发生时，树型视图发送一个WM_NOTIFY消息个父窗口，并在消息中附带传递一些附加信息。 
WM_NOTIFY
wParam ==控件的ID。因为该值不是唯一的，故我们不用它。我们使用NMHDR结构体中的hwndFrom或IDFrom成员变量。
lParam == 指向NMHDR结构体的指针。有一些控件可能传递一个指向更大一点的结构体的指针。但该结构体必须保证它的第一个成员变量是一个NMHDR型的变量。这样，您在处理lParam变量时，至少可以得到一个NMHDR型的变量。 下面我们来看NMHDR： 
NMHDR struct DWORD
hwndFrom DWORD ?
idFrom DWORD ?
code DWORD ?
NMHDR ends hwndFrom是发送WM_NOTIFY消息的控件的窗口句柄。
idFrom是发送WM_NOTIFY消息的控件的ID。 
code是控件发送给父窗口的数据。
树型视图发送给父窗口的通知消息以TVN_打头。 树型视图接收到的消息以TVM_打头，譬如：TVM_CREATEDRAGIMAGE。 树型视图发送TVN_XXX消息时在code变量中放入NMHDR型变量。父窗口发送TVM_消息来控制树型视图。 
在树型视图中加入项目在创建完树型视图后可以通过发送TVM_INSERTITEM消息往其中加入项目了。 
TVM_INSERTITEM
wParam = 0;
lParam = 指向结构体TV_INSERTSTRUCT的指针; 
您应当知道一些关于树型视图中的项目之间关系的一些术语。 一个项目可能是一个父亲、儿子或两者都是。父项目下含有子项目，而该父项目又有可能是其它项目的子项目。一个没有父项目的项目叫根项目。在树型视图中可能有多个根项目。现在我们来看看TV_INSERTSTRUCT结构体：
TV_INSERTSTRUCT STRUCT DWORD
hParent DWORD ?
hInsertAfter DWORD ?
ITEMTYPE <>
TV_INSERTSTRUCT ENDShParent = 父项目的句柄。如果该值为TVI_ROOT value或NULL，该项目插在树型视图的根部。
hInsertAfter = 应该插入在起后面的项目的句柄或下面的值： 
TVI_FIRST ==> 插在列表的头部。 TVI_LAST ==> 插在列表的尾部。 TVI_SORT ==> 按字母顺序插入。 ITEMTYPE UNION
itemex TVITEMEX <>
item TVITEM <>
ITEMTYPE ENDS 我们仅使用TVITEM。 
TV_ITEM STRUCT DWORD
imask DWORD ?
hItem DWORD ?
state DWORD ?
stateMask DWORD ?
pszText DWORD ?
cchTextMax DWORD ?
iImage DWORD ?
iSelectedImage DWORD ?
cChildren DWORD ?
lParam DWORD ?
TV_ITEM ENDS 该结构体根据消息类型，用来发送或接收关于一个树型视图的项目的有关信息。譬如：对于消息TVM_INSERTITEM，它用来指定插入树型视图控件的项目的属性。而对于消息TVM_GETITEM，该结构体用来填充关于选定项目的信息。
imask 用来指定TV_ITEM的那些成员变量有效。譬如，如果指定了TVIF_TEXT，这意味着pszText成员变量是有效的。您可以同时指定几个标志位。
hItem 是树型视图项目的句柄。每一个项目都有它自己的句柄，就像窗口一样。如果您想要操作一个项目，就必须选择它的句柄。
pszText 是一个字符串指针。它是项目的标签名。
cchTextMax仅在查询项目的名称时使用。由于在pszText中指定了指针，WINDOWS还要知道该缓冲去的大小。所以您必须给出该值。
iImage 和 iSelectedImage用来指定图象列表以及一个索引号。这样就知道当项目被选中或没被选中时用哪个图象来表示该项目。像资源管理器中左边窗口中的文件夹等小图表就是有这两个参数来决定的。 
为了在树型视图中插入一个项目，您必须至少设定hParent, hInsertAfter，另外您还要设定imask和pszText值。 
把图形加到图形视图中如果您想要在项目的名称左边显示图标的话，您必须创建一个图形列表，并且把它和树形视图相关联起来。您可以调用ImageList_Create来创建一个图形列表。 
ImageList_Create PROTO cx:DWORD, cy:DWORD, flags:DWORD, \
cInitial:DWORD, cGrow:DWORD 如果创建成功的话，该函数返回一个空的图象列表的句柄。
cx == 以像素为单位的图象的宽度。
cy == 以像素为单位的图象的高度。图象列表中的每一幅的高度都必须相同。否则WINDOWS会对您的图象进行裁剪，如果过大的话就可能裁剪成几小块。所以您必须指定相同大小的图象。
flags == 指定图象列表的图象的颜色深度。详细情况请参考WIN32 API 指南。
cInitial == 指定包含的图象的数目。Windows将依此来分配合适的内存。
cGrow == 在增加新图象是一次增加的数目。
图象列表不是窗口。仅仅是保存在那给其它的窗口使用的一种资源。 在图象列表产生后，您可以调用ImageList_Add来向其中加入图象。 
ImageList_Add PROTO himl:DWORD, hbmImage:DWORD, hbmMask:DWORD 如果该函数调用失败的话，返回-1。
himl == 图象列表的句柄。它是调用ImageList_Create时返回的值。
hbmImage == 加入图象列表的位图的句柄。您通常把位图保存在资源中，然后调用LoadBitmap来把它加载进来。 注意您没有必要指定该位图中包含的图象的数目。WINDOWS会根据它的大小，自动计算。
hbmMask == 掩码位图的句柄。如果没有使用掩码位图，可以忽略该值。 通常我们加入两种图象到图象列表中。一种时被选中时显示的图象，另一种时没被选中时显示的。
当图象列表准备就绪后，您可以发送消息TVM_SETIMAGELIST给树型视图来让图象列表和树型视图联系起来。 
TVM_SETIMAGELIST
wParam = 图象列表的状态，一共有两种： TVSIL_NORMAL 包含被选中和没有被选中两种状态的图象。 TVSIL_STATE 包含了用户自定义的状态的图象。 lParam = 图象列表的句柄。 检索树型视图的信息您可以通过发送消息TVM_GETITEM来检索图形视图的信息。 
TVM_GETITEM
wParam = 0
lParam =指向结构体TV_ITEM的指针。该结构体将用来得到相关的信息。
在发送该消息前必须设置成员变量imask的值，以便WINDOWS能告诉相关的信息。当然，最重要的是，您必须传递您想得到信息的项目的句柄。这就引起了一个问题，您如何得到项目的句柄？要保存所有项目的句柄吗？
答案是很简单的：没有必要。您可以发送消息TVM_GETNEXTITEM到树型视图以检索您想要得到其属性的项目的句柄。譬如：您可以查询第一个子项目的句柄、根目录的句柄、选中的项目的句柄等等。
TVM_GETNEXTITEM
wParam = 标志
lParam = 树型视图的句柄（仅仅当wParam的值是某些标志位时才是必须的）。 wParam中的值非常重要， 我解释如下： 
TVGN_CARET 选中的项目 TVGN_CHILD hitem参数指定项目的第一个子项目 TVGN_DROPHILITE 拖-拉操作的目的项目 TVGN_FIRSTVISIBLE 第一个可见项目 TVGN_NEXT 下一个同级项目 TVGN_NEXTVISIBLE 下一个可见项目，指定的项目必须可见。发送消息TVM_GETITEMRECT 来决定项目是否可见 TVGN_PARENT 指定项目的父项目 TVGN_PREVIOUS 前一个同级项目 TVGN_PREVIOUSVISIBLE 前一个可见项目，指定的项目必须可见。发送消息TVM_GETITEMRECT 来决定项目是否可见 TVGN_ROOT 根项目 由此您可以通过发送该消息来得到项目的句柄，然后在发送消息TVM_GETITEM时在结构体变量TV_ITEM的成员变量hItem中放入该项目的句柄就可以得到关于该项目的有关信息了。 
在树型视图中进行拖-拉操作也就是因为这一部分我才决定写这课教程。当我按照InPrise公司的WIN32帮助来运行例子时，发现它的帮助中缺少真正重要的信息。我只有通过自己做实验，最后总算弄明白来个中来由。希望您不要和我一样再去走这些弯路，下面我把我所知的在树型视图中进行拖-拉操作的步骤描述如下： 
当用户要拖动一个项目时，树型视图控件会给它的父窗口发送TVN_BEGINDRAG通知消息。您可以在此处创建表示项目处在拖动操作中的图象，这可以通过发送TVM_CREATEDRAGIMAGE消息给树型视图，让其为目前使用的图象产生一副缺省的图象来实现。树型视图控件将创建一个图象列表，其中仅包含一副在拖动中显示的图象，图象列表创建后，您可以得到它的句柄。 在拖拉的图象生成后，您可以通过调用ImageList_BeginDrag来指定拖动图象的热点位置。 ImageList_BeginDrag PROTO himlTrack:DWORD, \
iTrack:DWORD , \
dxHotspot:DWORD, \
dyHotspot:DWORD
himlTrack 是包含了拖拉时显示的图象的图象列表的句柄 
iTrack 是选中的图象在图象列表中的索引号。
dxHotspot 因为在拖动中该图象被用来取代光标，所以我们必须指定图象中的哪一点是光标的左上角的位置。dxHotspot是水平相对位置。 
dyHotspot 是垂直相对位置。
iTrack等于0。如果您要想光标的热点在拖拉中显示的图象的左上角，把dxHotspot和dyHotspot都设成0。 当拖拉的图象要显示时，我们调用ImageList_DragEnter 在树型视图中显示该图象。 ImageList_DragEnter PROTO hwndLock:DWORD, x:DWORD, y:DWORD
hwndLock 是进行拖拉中的窗口的句柄，拖拉的动作限制在该窗口中。
x 和 y是在拖拉时显示图象的初始位置的坐标值。这些值是相对于窗口的左上角而不是客户区的左上角。 既然可以显示拖动中的图象了，我们就要处理拖动操作了。在这里有一个小问题。我们监视拖动是通过监视鼠标光标的移动来实现的，譬如在移动时我们通过捕获WM_MOUSEMOVE消息来得到移动中的坐标位置，通过捕获WM_LBUTTONUP消息来获知用户的放下操作。但这时如果鼠标光标移过子窗口时父窗口就无法再得到鼠标光标的移动以及鼠标的按键消息了。解决办法是调用SetCapture函数了锁定鼠标事件，这样无论鼠标移到那里和有什么动作，我们的窗口都可以知道了。 在处理WM_MOUSEMOVE消息时，您可以调用ImageList_DragMove来更新图象移动的轨迹。 该函数可以移动拖放操作中的图象位置。另外，如果您想让移动中的图象经过某些项目时高量度显示，可以调用TVM_HITTEST 来确定是否经过某个项目的上面。如果是的话，您可以发送TVM_SELECTITEM消息并设置 TVGN_DROPHILITE标志位使得那个项目高亮度显示。注意：在发送消息TVM_SELECTITEM前，您必须先隐藏图象列表，否则会留下非常难看的轨迹。要隐藏拖动中的图象可以调用ImageList_DragShowNolock，在显示完高亮度的图象后再调用该函数以让拖动中的图象再正常显示。 当用户释放主键后，您必须做几件事。 如果您在高亮度显示的时候释放鼠标主键（表示您想把该项目加到此处），您必须使该项目变成正常地显示，这可以通过发送消息TVM_SELECTITEM消息并设置标志位TVGN_DROPHILITE来实现，只是这时lParam必须为0。如果您不让高亮度显示的项目恢复正常，那就会发生一个奇怪的现象：当您再选择另外的项目时，那个项目的图象会包含在一个正方形中，当时高亮度显示的项目依旧是上一个项目。接下来必须调用ImageList_EndDrag和ImageList_DragLeave。还有调用ReleaseCapture来释放捕获的鼠标。如果您创建了一个图象列表，那还要调用calling ImageList来将它销毁，在拖放操作结束后您可以进行另外其它的操作。
树型视图控件的数据结构
1. HTREEITEM句柄
树形控件中的每个节点都可以由一个HTREEITEM类型的句柄表示。我们通过CTreeCtrl类的成员函数对树进行访问和操作时，很多时候都要用到HTREEITEM句柄。
2. TVITEM结构体
TVITEM结构体描述了树形控件节点的属性，定义如下：
typedef struct tagTVITEM { 
UINT mask; // 包含一些掩码位（下面的括号中列出）的组合，用来表明结构的哪些成员是有效的 
HTREEITEM hItem; // 树节点的句柄(TVIF_HANDLE) 
UINT state; // 树节点的状态(TVIF_STATE) 
UINT stateMask; // 状态的掩码组合(TVIF_STATE) 
LPTSTR pszText; // 树节点的标签文本(TVIF_TEXT) 
int cchTextMax; // 标签文本缓冲区的大小(TVIF_TEXT) 
int iImage; // 树节点的图像索引(TVIF_IMAGE) 
int iSelectedImage; // 选中项的图像索引(TVIF_SELECTEDIMAGE) 
int cChildren; // 表明节点是否有子节点，为1则有，为0则没有(TVIF_CHILDREN) 
LPARAM lParam; // 一个32 位的附加数据(TVIF_PARAM) 
} TVITEM, *LPTVITEM; 
此结构体中多个元素涉及到了图像和状态等，有必要具体解释下。
树形控件节点需要显示图标时，就要为树形控件关联一个图像序列，上面的iImage成员就代表了该结构体对应的树节点的图标在图像序列中的索引，iSelectedImage则代表该树节点被选中时显示的图标在图像序列中的索引。
stateMask用来说明要获取或设置树节点的哪些状态。下面是state和stateMask的一些常用值及含义：
state 对应的stateMask 含义 
TVIS_CUT TVIS_CUT 节点被选择用来进行剪切和粘贴操作
TVIS_DROPHILITED TVIS_DROPHILITED 节点成为拖动操作的目标
TVIS_EXPANDED TVIS_EXPANDED 节点的子节点被展开
TVIS_EXPANDEDONCE TVIS_EXPANDEDONCE 节点的子节点曾经被展开过
TVIS_SELECTED TVIS_SELECTED 节点被选中
lParam在实际开发中常用来存放与树节点有关的附加数据。
3. NMTREEVIEW结构体
NMTREEVIEW结构体中包含了树形控件通知消息的相关信息。树形控件的大多数通知消息都会带有指向该结构体的指针。NMTREEVIEW结构体的定义如下：
typedef struct tagNMTREEVIEW { 
NMHDR hdr; // 标准的NMHDR结构 
UINT action; // 表明是用户的什么行为触发了该通知消息 
TVITEM itemOld; // 原节点的属性 
TVITEM itemNew; // 新节点的属性 
POINT ptDrag; // 事件发生时鼠标的客户区坐标 
} NMTREEVIEW, *LPNMTREEVIEW; 
4. TVINSERTSTRUCT结构体
向树形控件中插入新节点时需要用到TVINSERTSTRUCT结构体，它常与TVM_INSERTITEM消息一起使用。定义如下：
typedef struct tagTVINSERTSTRUCT { 
HTREEITEM hParent; // 父节点的句柄 
HTREEITEM hInsertAfter; // 指明插入到同层中哪一项的后面 
#if (_WIN32_IE >= 0x0400) 
union 
{ 
TVITEMEX itemex; 
TVITEM item; 
} DUMMYUNIONNAME; 
#else 
TVITEM item; // 要添加的新节点的属性 
#endif 
} TVINSERTSTRUCT, *LPTVINSERTSTRUCT; 
若hParent成员为TVI_ROOT或NULL，那么新节点将被作为树的根节点插入。hInsertAfter除了可以是某个节点的句柄，还可以有四种取值：TVI_FIRST（插入到树形控件的最前面）、TVI_LAST（插入到树形控件的最后面）、TVI_ROOT（作为根节点插入）和TVI_SORT（按字母顺序插入）。
5. NMTVDISPINFO结构体
NMTVDISPINFO结构体中包含了与树节点的显示有关的信息。定义如下：
typedef struct tagNMTVDISPINFO { 
NMHDR hdr; 
TVITEM item; 
} NMTVDISPINFO, *LPNMTVDISPINFO; [1]
树型视图控件CTreeCtrl类的主要成员函数
CImageList* SetImageList(CImageList * pImageList,int nImageListType);
如果树节点需要显示图标时，则必须先创建一个CImageList类的对象，并为其添加多个图像组成一个图像序列，然后调用SetImageList函数为树形控件设置图像序列，在用InsertItem插入节点时传入所需图像在图像序列中的索引即可。后面的例子中会演示。参数pImageList为指向图像序列类CImageList的对象的指针，若为NULL则删除树形控件的所有图像。参数nImageListType指定图像序列的类型，可以是TVSIL_NORMAL（普通图像序列）或TVSIL_STATE（状态图像序列，用图像表示节点的状态）。
UINT GetCount( ) const;
获取树形控件中节点的数量。
DWORD_PTR GetItemData(HTREEITEM hItem) const;
获取树形控件中某个指定节点的附加32位数据。参数hItem为指定的树节点的句柄。
BOOL SetItemData(HTREEITEM hItem,DWORD_PTR dwData);
为树形控件中某个指定节点设置附加的32位数据。参数hItem同上，dwData为要设置的32位数据。
CString GetItemText(HTREEITEM hItem) const;
获取树形控件中某个指定节点的标签文本。参数hItem同上。返回值是包含标签文本的字符串。
BOOL SetItemText(HTREEITEM hItem,LPCTSTR lpszItem);
为树形控件中某个指定节点设置标签文本。参数hItem同上，lpszItem为包含标签文本的字符串的指针。
HTREEITEM GetNextSiblingItem(HTREEITEM hItem) const;
获取树形控件中某个指定节点的下一个兄弟节点。参数hItem同上。返回值是下一个兄弟节点的句柄。
HTREEITEM GetPrevSiblingItem(HTREEITEM hItem) const;
获取树形控件中某个指定节点的上一个兄弟节点。参数hItem同上。返回值是上一个兄弟节点的句柄。
HTREEITEM GetParentItem(HTREEITEM hItem) const;
获取树形控件中某个指定节点的父节点。参数hItem同上。返回值是父节点的句柄。
HTREEITEM GetRootItem( ) const;
获取树形控件根节点的句柄。
HTREEITEM GetSelectedItem( ) const;
获取树形控件当前选中节点的句柄。
BOOL DeleteAllItems( );
删除树形控件中的所有节点。删除成功则返回TRUE，否则返回FALSE。
BOOL DeleteItem(HTREEITEM hItem);
删除树形控件中的某个节点。参数hItem为要删除的节点的句柄。删除成功则返回TRUE，否则返回FALSE。
HTREEITEM InsertItem(LPCTSTR lpszItem,int nImage,int nSelectedImage,HTREEITEM hParent = TVI_ROOT,HTREEITEM hInsertAfter = TVI_LAST);
在树形控件中插入一个新节点。参数lpszItem为新节点的标签文本字符串的指针，参数nImage为新节点的图标在树形控件图像序列中的索引，参数nSelectedImage为新节点被选中时的图标在图像序列中的索引，参数hParent为插入节点的父节点的句柄，参数hInsertAfter为新节点的前一个节点的句柄，即新节点将被插入到hInsertAfter节点之后。
BOOL SelectItem(HTREEITEM hItem);
选中指定的树节点。参数hItem为要选择的节点的句柄。若成功则返回TRUE，否则返回FALSE。[2]