最近使用树性控件,遇到一些问题,于是查了一下MSDN,感觉阐述的很清楚,很明白,于是索性把它翻译过来,翻译的不太好,各位别见笑 哦.
树形控件 树形控件是一个用来显示具有层次关系一系列条目的窗口,例如,文档上的标题,索引上的条目,或者磁盘上的文件和目录等。控件的每一项由一个label和额外的一个位图组成,每一个条目可以有一系列子条目和它相关联。用户可以通过点击一个条目,从而展开或者收缩和它相关联的字条目。
以下主题将被讨论:
 关于树形控件
 使用树形控件
 在Internet Explorer中更新树形控件
 相关的主题
关于树形控件
利用CreateWindowEx函数指定可以创立一个树形控件,但你必须指定WC_TREEVIEW标志位。当公共控件动态链接库被加载时,这个控件类就被注册了。为了确保前面的Dll文件是否加载了,可以在应用程序里包括InitCommonControls()函数来检查。下面图解显示了树形控件的用法。
 
当创建一个Tree-View控件后,你就可以通过向控件发送消息来添加,删除,安排或者操纵控件中的各项了。每一个消息都有一个或多个相应的宏定义,你可以使用这些宏来替代直接发送消息的操作。
树形控件风格
树形控件风格控制一个树形控件外观的各个方面。可以在创立控件时指定控件的初始风格。在创建控件之后,你也可以通过SetWindowLong和GetWindowLong来获取或改变控件的风格。
TVS_HASLINES风格:通过画一些连接子节点和它们的父节点的连线增强了树形控件的图形化外观。但是,此风格并没有连接处于跟目录的各节点。如果连接处于跟目录的各节点,必须要联合TVS_HASLINES和 TVS_LINESATROOT风格。
用户可以通过双击父节点来展开或收缩子节点。树形控件中的TVS_HASBUTTONS风格,用来在每一个父节点的左边增加一个按钮。用户就可以只点击这个按钮一次就可以实现双击时的效果。但TVS_HASBUTTONS没有在根节点间添加按钮,如果要使处于跟目录的各节点添加按钮,就必须联合TVS_HASLINES,TVS_LINESATROOT和TVS_HASBUTTONS标志。
TVS_EDITLABELS风格:使用户可以编辑节点上的标签(文本)。
TVS_SHOWSELALWAYS风格:当树形控件失去焦点时,使用这种风格选中的节点保留选中的状态。
TVS_CHECKBOXES风格:在每一个节点的左边创建一个复选框。如果你想使用TVS_CHECKBOXES风格,你必须在创建树形控件之后和往树形控件加载数据之前通过SetWindowLong函数设置TVS_CHECKBOXES风格。
父节点和子节点
树形控件中任何一个节点都可以有一系列子节点和它相关联。如果一个节点有一个或多个子节点就叫做父节点。子节点显示在父节点下面并且在水平上缩进以表示它是父节点的下级节点。在这种层次的最上层没有父节点出现的节点就叫做根节点。
我们可以通过给控件发送TVM_INSERTITEM消息来给树形控件添加新节点。这个消息将返回能够唯一标示此节点的句柄,返回句柄的类型是HTREEITEM类型。当添加一个节点时,你必须指定父节点的句柄。如果在TVINSERTSTRUCT中不指定父节点的句柄,而是指定NULL或TVI_ROOT值时,此节点将被当作根节点插入。
无论在任何时候,父节点的字节点们的状态要么是展开,要么是收缩状态。当状态是展开时,其子节点将被显示在父节点的下方。当状态是收缩时,其子节点将不会显示。当用户使用鼠标双击父节点时,或者父节点指定了TVS_HASBUTTONS风格,当用户点击和父节点相关联的按钮时,这些子列表将会在展开和收缩之间自动的切换。应用程序可以通过发送TVM_EXPAND消息来展开或收缩子节点。
当父节点的子节点将要被展开或者收缩时,树形控件将会向父窗口发送一个TVN_ITEME XAPNDING通知消息。这个通知给应用程序阻止改变或者根据子节点的状态来设置父节点的属性提供了一个机会。在状态改变之后,树形控件将向父窗口发送一个TVN_ITEMEXPANDED通知消息。
当子节点展开时,它们将相对于父节点缩进排列。你可以通过TVM_SETINDENT消息设置缩进量的大小或者通过TVM_GETINDENT消息来取回当前的缩进量。
树形控件是使用进程中的堆来分配内存创建控件的。所以,节点的最大数量取决于进程中堆的内存容量。
节点标签
当往树形控件添加一个节点时,你可以指定此节点的标签文本。消息TVM_INSERTITEM中就包括了一个TVITEM结构,此结构定义了节点的各种属性,其中就包含标签文本的字符串。
树形控件为每一个节点分配内存来存储它们。节点的标签文本占据了这些内存中很大一部分。如果你的应用程序维护一份树形控件标签文本的拷贝,你就可以在TVITEM的成员pszText中指定LPSTR_TEXTCALLBACK值以用来替代给树形控件传替真正的字符串,从而达到降低树形控件对内存的需求。当使用LPSTR_TEXTCALLBACK样式时 ,每当节点需要重新绘制时,会致使树形控件取回节点的标签文本。为了取回文本,树形控件需要发送一个TVN_GETDISPINFO通知消,这个消息就包含了一个NMTVDISPINFO结构的地址。父窗口必须给这个结构的各种成员赋予适当的值。
树形控件标签的编辑
如果控件指定了TVS_EDITLABELS样式,用户就可以直接编辑树形控件节点的文本标签。当节点获得焦点时,用户可以点击节点开始进行编辑。应用程序可以使用TVM_EDITLABEL消息进行标签编辑。当编辑开始时,或者编辑操作取消或完成时,树形控件将会通知父窗口。当编辑完成时,父窗口负责更新节点的标签,如果适当的话。
当标签编辑开始时,树形控件会向他的父窗口发送一个TVN_BEGINLABELEDIT通知消息。通过处理这个消息,应用程序可以允许编辑一些标签或者阻止对其他标签的编辑。当弹回零时允许编辑,返回非零时阻止编辑。
当标签编辑取消或完成时,树形控件将向她的父窗口发送一个TVN_ENDLABELEDIT通知小子,参数lparam包含了NMTVDIPINFO结构的地址。参数item是一个TVITEM结构,这个结构标识了节点并且包含编辑过的文本。父窗口负责更新节点的标签。如果编辑取消了,TVITEM成员pszText将指向零。
在编辑期,主要响应TVN_BEGINLABELEDIT通知消息。使用TVM_GETEDITCONTROL消息,你可以取回一个指向编辑控件的句柄。通过对编辑空间发送一个EM_SETLIMITTEXT消息,可以限定用户可以录入文本的长度或者指定编辑控件一个子集去截取并且去掉无效字符。注意,仅仅在TVN_BEGINLABELEDIT消息发送后,编辑控件将会被显示。
树形节点的位置
通过使用TVM_INSERTITEM消息往树形控件添加节点时,节点的初始位置将会被指定。这消息包含了一个TVINSERTSTRUCT结构,这个结构指定了一个指向其父节点的句柄以及一个指向新节点插入后的下一节点。第二个句柄必须标识给定父节点的子节点或者指定下列值之一:TVI_FIRST,TVI_LAST,TVI_SORT。
当TVI_FIRST或者TVI_LAST指定时,树形控件将把新节点插入到父节点钟所有的最开始或者最后。当TVI_SORT指定时,树形控件根据节点标签文本的字母顺序,把节点插入子节点中的合适的位置 。
你可以使用TVM_SORTCHILDEN消息对父节点的所有子节点进行排序。当你使用这个消息时,你可以指定基于程序定义的一个回调函数,以便树形控件在两个子节点相对次序需要调整时进行调用。这个回调函数接受到对子节点进行比较的两个32位基于程序定义的值和一个当你发送TVM_SORTCHILDRENCB指定的32位值。
树形控件节点状态总观
树形控件的每个节点都有个当前状态,每个节点的状态信息包含了一些位标志的集合还有图像列表的索引,这些索引显示节点状态的图像和覆盖图像。这些位标志指定了节点是否被选中,或者失效,展开,等等。在极大程度上,树形控件可以自动的设置节点的状态以反映用户的动作,像节点的选中。但是,你同样能够设置节点的状态通过使用TVM_SETITEM消息,并且可以使用TVM_GETITEM消息取回节点的当前状态。关于节点状态的完全清单,请参考树形控件节点的状态。
节点的当前状态是被TVITEM结构中的state成员指定的。树形控件可能改变节点的状态去反映用户的操作,像选中一个节点,把焦点移到一个节点。另外,应用程序可能需要改变节点的状态以便失效或隐藏节点或者指定一副覆盖图或状态图。
当你指定或改变一个节点的状态时,TVITEM成员statemask指定了哪个状态将被设置,成员state包含了这些的新值。
为了设置一个节点的覆盖图像,statemask必须包含TVIS_OVERPAYMASK值,并且state必须包含以一为基的覆盖图像索引,这个索引必须使用宏INDEXTOOVERLAYMASK左移8位。如果没有指定覆盖图像,这个索引可以为零。
状态图被显示为节点的图标用来表明程序自定义的状态。这些状态图是通过发送TVM_SETIMAGELIST消息放置在一个状态图像列表里。为了设置一个节点状态图,必须在TVITEM的成员statemask里包含TVIS_STATEIMAGEMASK值。State成员的12位到15位指定了图像列表里被重化图像的索引。
为了能够设置状态图的索引,可以使用宏INDEXTOSTATEIMAGEMASK。这个宏的作用是把一个索引响应的转化为state成员的第12位到15位。可以把索引设置为零,用来表示没有此节点没有状态图。这说明了状态图列表里索引为零的图就不可以用作一个节点的状态图。可以使用TVIS_STATEIMAGEMASK标志把state成员的第12位到15位分离出来。关于覆盖和状态图的更详细的信息,请参考树形控件的图像列表。
选中节点
当选中节点发生改变时,树形控件就会父窗口发送一个TVN_SELCHANGEING和TVN_SELCHANGED通知消息。这两个通知都包含一个值,这个值指定了导致选中状态的改变是因为鼠标,还是键盘引起。这个通知也包含了获取焦点的节点信息以及失去焦点的节点信息。你能够使用这些信息去设置节点的属性,取决于节点的选中状态。当返回TRUE时,阻止选中节点状态的改变,返回FALSE允许状态的改变。
节点信息树形控件支持很多消息,这些消息可以取回控件中节点的信息。
消息TVM_GETITEM能够取回一个节点的句柄和属性。一个节点的属性包含了它包含了他现在的状态,以及节点选中或未选中时 位图图像在控件图形列表中的索引。以及一个标志表明这个节点是否是有子节点,节点标签的字符串的地址,以及节点的应用程序定义的32位的值。
消息TVM_GETNEXTITEM取回树形控件的节点,这个节点和当前节点具有特定的关系。这个消息可以取回一个节点的父亲,上一个或下一个可视节点,第一个节点,等等。
消息TVM_GETITEMRECT取回一个关于一个节点边界的特定的矩形。消息TVM_GETCOUNT和消息TVM_GETVISIBLECOUNT取回树形控件中节点的数量和在可以在树形控件可以全部显示的节点的数量。相对的来说。你能够使用消息TVM_ENSUREVISIBLE消息确保特定部分的节点可以显示。
节点的图像列表
树形控件中每一个节点都可以有四幅图像和它相关联。
 一副图像,比如象一副打开的文件图像,当节点选中时显示。
 一副图像,比如像一副关闭的文件图像,当节点没有选中时显示。
 覆盖图像是一副在选中或没有选中时高亮重画的图像。
 状态图,是额外一副显示在选中或没有选中左边的图像。你能够使用状态图,比如选中和清除选中的复选框,用来说明应用程序定义的节点状态。
默认情况下,树形控件并不显示他的节点图像。为了能够显示节点图像。必须建立一个图像列表并且把它们和树形控件向关联起来。关于更详细介绍,请看Image Lists。
树形控件可以有两个图像列表。一个正常图像列表和一个状态图像列表。正常图像列表包含了选中,未选中的,覆盖的图像,一个状态图像列表存储了状态图。可以使用ImageList Create函数去建立一个图像列表。使用其他的Image List函数往图像列表中添加位图。然后,就可以使用TVM_SETIMAGELIST消息把图像列表和控件向关系起来了。这个TVM_GETIMAGELIST消息取回一个指向一个树形控件的图像列表的句柄。当你需要往图像列表添加更多的图像时,这消息很有用的。
除了选中和未选中的图像,树形控件的正常图列表能够容纳四幅覆盖图像。覆盖被基于1的索引所标识,用来设计在选中和未选中图像的高亮显示。为了给图像列表里的一个图像分配一个覆盖索引,可以调用SetOverlayImage函数。
默认情况下,所有节点选中或为选中时,都显示正常图像列表中的第一幅图。而且,默认情况下,节点没有显示覆盖图像或者 状态图像。你可以使用TVM_INSERTITEM或TVM_SETTEIM消息改变节点的默认行为。这些消息使用了TVITEM结构指定了节点的图像索引。
为了指定一个节点选中或未选中的图像,需要在TVITEM结构成员mask中设置

解决方案 »

  1.   

    TVIF_SELECTEDIMAGE和TVIF_IMAGE位,并且指定在成员iSelectImage和iImage指定图像列表中的索引。这些设置将会导致每当控件中节点将要 重画时,控件就要查询它的父窗口以获取一个图像列表。控件可以发送一个TVN_GETDISPINFO通知消息取回这些索引。
    为了使节点和一个覆盖图像相关联,可是使用INDEXTOOVERLAPYMASK宏在节点的TVITEM结构的成员state制定一个覆盖标志索引。你必须在成员stateMask中设置TVIS_OVERLAYMASK。覆盖标志索引是基于1的,当索引是0时,表明了当前没有任何覆盖图像被指定。
    状态图存储在一个独立的图像列表中并且由他们的索引来标识。为了指定一个状态图像列表,必须 发送一个 TVM_SETIMAGELIST消息。跟列表视图控件不一样,列表试图控件使用基于1的索引来标识状突图像,树形控件的状态图像则是使用基于零的索引。
    但是,当索引是0的时候,表明了此节点没有一个状态图。因此,索引为0的图像不能作为状态图来使用。有关于节点状态以及节点状态图的更深的讨论,参见Tree-View Item States OverView
    拖放操作
    当用户开始拖曳一个节点时,树形控件将会通知父窗口。当用户使用左键拖曳一个节点时,父窗口将会收到一个TVN_BEGINDRAG通知信息;当用户使用右键拖曳一个节点时,父窗口将会收到一个TVN_BEGINRDRAG通知信息。你可以通过给树形控件设置TVS_DISABLEDRAGDROP样式阻止树形控件发送这些通知信息。
    通过使用TVM_CREATEDRAGIMAGE消息,可以得到一幅用于显示拖曳操作的图像。树形控件将根据被拖曳节点的标签创建一幅用来拖曳操作的位图。然后树形控件将创立一个图像列表,并且添加上述的位图,并返回一个指向图像列表的指针。
    你必须自己提供用来产生实际拖放操作的代码。这些代码包括图像列表有拖放能力的函数,以及处理WM_MOUSEMOVE和WM_LBUTTONUP(WM_RBUTTONUP)消息。
    如果树形控件的节点是拖放操作的目的节点,你必须知道什么时候鼠标指向了一个目的节点。不过,你可以使用TVM_HITTEST消息找出目标节点。必须指定一个TVJOTTESTINFO结构的地址,这个结构包含了当前鼠标的指向的坐标。当函数SendMessage返回时,这个结构包含了一个标志,这个标志指向了一个鼠标相对于树形控件的位置。如果这个指示器指向树形控件的一个节点时,这个结构就包含了指向这个节点的一格句柄。
    当通过消息TVM_SETITEM设置state成员一个TVIS_DROPHILIITED值时,你就可以指定拖放操作的目的节点。如果一个节点有TVIS_DROPHILITED值时,就表明了它是拖放操作的目的节点。
    关于树形控件的通知信息
    树形控件将以WM_NOTIFY消息的形式向父窗口发送下列通知信息
    Notification Description
    TVN_BEGINDRAG Signals the start of a drag-and-drop operation.
    TVN_BEGINLABELEDIT   Signals the start of in-place label editing.
    TVN_BEGINRDRAG Signals that the right mouse button has started a drag-and-drop operation.
    TVN_DELETEITEM Signals the deletion of a specific item.
    TVN_ENDLABELEDIT Signals the end of label editing.
    TVN_GETDISPINFO Requests information that the tree-view control requires to display an item.
    TVN_ITEMEXPANDED Signals that a parent item's list of child items was expanded or collapsed.
    TVN_ITEMEXPANDING Signals that a parent item's list of child items is about to be expanded or collapsed.
    TVN_KEYDOWN Signals a keyboard event.
    TVN_SELCHANGED Signals that the selection has changed from one item to another.
    TVN_SELCHANGING Signals that the selection is about to be changed from one item to another.
    TVN_SETDISPINFO Notifies a parent window that it must update the information it maintains for an item.
    树形控件的默认消息处理过程
    这部分描述了树形控件的窗口消息处理过程。关于树形控件的特定消息的处理将会在本文当的其他地方介绍。
    Message Processing performed
    WM_COMMAND Processes the EN_UPDATE and EN_KILLFOCUS edit control notification messages and forwards all other edit control notifications to the parent window. There is no return value.
    WM_CREATE Allocates memory and initializes internal data structures. It returns zero if successful, or -1 otherwise.
    WM_DESTROY Frees all system resources associated with the control. It returns zero.
    WM_ENABLE Enables or disables the control.
    WM_ERASEBKGND Erases the window background using the current background color for the tree-view control. It returns TRUE.
    WM_GETDLGCODE Returns a combination of the DLGC_WANTARROWS and DLGC_WANTCHARS values.
    WM_GETFONT Returns the handle to the current label font.
    WM_HSCROLL Scrolls the tree-view control. It returns TRUE if scrolling occurs, or FALSE otherwise.
    WM_KEYDOWN Sends the TVN_KEYDOWN notification message to the parent window for all keys. Sends the NM_RETURN (tree view) notification message when the user presses the ENTER key. It moves the caret when the user presses the direction keys or the PAGE UP, PAGE DOWN, HOME, END, or BACKSPACE key. It scrolls the tree-view control when the user presses the CTRL key in combination with those keys. It returns TRUE if a key is processed, or FALSE otherwise.
    WM_KILLFOCUS Repaints the focused item, if any, and sends an NM_KILLFOCUS (tree view) notification message to the parent window.
    WM_LBUTTONDBLCLK Cancels label editing and, if an item was double-clicked, sends the NM_DBLCLK notification message to the parent window. If the parent window returns 0, the tree-view control toggles the expanded state of the item, sending the parent window the TVN_ITEMEXPANDING and TVN_ITEMEXPANDED notification messages. There is no return value.
    WM_LBUTTONDOWN Toggles the expanded state if the user clicked the button associated with a parent item. If the user clicked an item label, the tree-view control selects and sets the focus to the item. If the user moves the mouse before releasing the mouse button, the tree-view control begins a drag-and-drop operation. There is no return value.
    WM_PAINT Paints the invalid region of the tree-view control. It returns zero. If the wParam parameter is non-NULL, the control assumes that the value is a handle to a device context (HDC) and paints using that device context.
    WM_RBUTTONDOWN Checks to see if an item was clicked and a drag operation was begun. If the operation has begun, it sends a TVN_BEGINRDRAG notification message to the parent window and highlights the drop target. Otherwise, it sends an NM_RCLICK notification message to the parent window. There is no return value.
      

  2.   

    WM_RBUTTONDOWN Checks to see if an item was clicked and a drag operation was begun. If the operation has begun, it sends a TVN_BEGINRDRAG notification message to the parent window and highlights the drop target. Otherwise, it sends an NM_RCLICK notification message to the parent window. There is no return value.
    WM_SETFOCUS Repaints the focused item, if any, and sends an NM_SETFOCUS notification message to the parent window.
    WM_SETFONT Saves the specified font handle and repaints the tree-view control using the new font.
    WM_SETREDRAW Sets or clears the redraw flag. The tree-view control is redrawn after the redraw flag is set. It returns zero.
    WM_SIZE Recomputes internal variables that depend on the size of the tree-view control's client area. It returns TRUE.
    WM_STYLECHANGED Cancels label editing and redraws the tree-view control using the new styles. It returns zero.
    WM_SYSCOLORCHANGE Redraws the tree-view control using the new color if the redraw flag is set. There is no return value.
    WM_TIMER Begins editing an item label. If the user clicks the label of the focused item, the tree-view control sets a timer instead of entering edit mode immediately. The timer makes it possible for the tree view to avoid entering edit mode if the user double-clicks the label. It returns zero.
    WM_VSCROLL Scrolls the tree-view control. It returns TRUE if scrolling occurs, or FALSE otherwise.
    使用树形控件
    本部分将会使用例子阐述下列几项任务
     建立树性空间
     初始化图像列表
     添加节点
     拖曳操作
    1. 创立树性控件
    为了建立一个树形控件,可以使用函数CreateWindowEx,并且在窗口类别中指定WC_TREEVIEW值。当公共控件动态链接库加在时,树性控件将会在应用程序的地址空间里注册。为了确保动态链接库的加载,可以使用InitCommonControls函数
    下列例子建立一个树形控件,这个树形控件的大小正好填满父窗口的区域。并且使用了基于程序定义的函数为控件指定了一个图像列表和为空间添加了节点。
    // CreateATreeView - creates a tree-view control. 
    // Returns the handle to the new control if successful,
    // or NULL otherwise. 
    // hwndParent - handle to the control's parent window. 
    // lpszFileName - name of the file to parse for tree-view items.HWND CreateATreeView(HWND hwndParent, LPSTR lpszFileName) 

        RECT rcClient;  // dimensions of client area 
        HWND hwndTV;    // handle to tree-view control     // Ensure that the common control DLL is loaded. 
        InitCommonControls();     // Get the dimensions of the parent window's client area, and create 
        // the tree-view control. 
        GetClientRect(hwndParent, &rcClient); 
        hwndTV = CreateWindowEx(0,
                                WC_TREEVIEW,
                                "Tree View",
                                WS_VISIBLE | WS_CHILD | WS_BORDER | TVS_HASLINES, 
                                0, 
                                0, 
                                rcClient.right, 
                                rcClient.bottom,
                                hwndParent, 
                                (HMENU)ID_TREEVIEW, 
                                g_hinst, 
                                NULL);     // Initialize the image list, and add items to the control. 
        // InitTreeViewImageLists and InitTreeViewItems are application- 
        // defined functions. 
        if (!InitTreeViewImageLists(hwndTV) || 
                    !InitTreeViewItems(hwndTV, lpszFileName))
        { 
            DestroyWindow(hwndTV); 
            return FALSE; 
        } 
        return hwndTV;
      

  3.   

    2. 初始化图像列表
    树形控件的每个节点可以有两幅和相关联的图像。当一个节点被选中时,显示一幅图像,没有被选中时显示另外一幅图像。为了在树形控件之中能够包含这些图像,首先,需要使用Image Lists函数建立一个图像列表,并且初始化它。可以通过使用TVM_SETIMAGELIST消息时树形控件和图像列表关联起来。
    下面的例子建立了一个图像列表,并且在列表之中添加了3幅图像,最后使树形控件和图像列表关联起来。
    例子:
    // InitTreeViewImageLists - creates an image list, adds three bitmaps 
    // to it, and associates the image list with a tree-view control. 
    // Returns TRUE if successful, or FALSE otherwise. 
    // hwndTV - handle to the tree-view control. 
    //
    // Global variables and constants 
    // g_nOpen, g_nClosed, and g_nDocument - integer variables for 
    // indexes of the images. 
    // CX_BITMAP and CY_BITMAP - width and height of an icon. 
    // NUM_BITMAPS - number of bitmaps to add to the image list. BOOL InitTreeViewImageLists(HWND hwndTV) 

        HIMAGELIST himl;  // handle to image list 
        HBITMAP hbmp;     // handle to bitmap     // Create the image list. 
        if ((himl = ImageList_Create(CX_BITMAP, 
                                     CY_BITMAP,
                                     FALSE, 
                                     NUM_BITMAPS, 0)) == NULL) 
            return FALSE;     // Add the open file, closed file, and document bitmaps. 
        hbmp = LoadBitmap(g_hinst, MAKEINTRESOURCE(IDB_OPEN_FILE)); 
        g_nOpen = ImageList_Add(himl, hbmp, (HBITMAP)NULL); 
        DeleteObject(hbmp);     hbmp = LoadBitmap(g_hinst, MAKEINTRESOURCE(IDB_CLOSED_FILE)); 
        g_nClosed = ImageList_Add(himl, hbmp, (HBITMAP)NULL); 
        DeleteObject(hbmp);     hbmp = LoadBitmap(g_hinst, MAKEINTRESOURCE(IDB_DOCUMENT)); 
        g_nDocument = ImageList_Add(himl, hbmp, (HBITMAP)NULL); 
        DeleteObject(hbmp);     // Fail if not all of the images were added. 
        if (ImageList_GetImageCount(himl) < 3) 
            return FALSE;     // Associate the image list with the tree-view control. 
        TreeView_SetImageList(hwndTV, himl, TVSIL_NORMAL);     return TRUE; 
    } 3. 添加树形节点
    通过发送TVM_INSERTITEM消息,可以往树形控件中添加一个节点。这个消息包含了一个TVINSERTSTRUCT结构的地址,指定了其父节点,以及新节点插入哪个节点的后面,和一个TVITEM结构包含了此节点的各种属性。这些属性包括了此节点的标签,选中和未选中时的图像,和一个基于应用的的32位的值。
    这部分的实例显示了根据文本中的信息怎样创造一个目录。这个例子包含两个函数。第一个函数查找一个文件头,当他找到了 ,就抽取文件头的文本和表明文件头所处的层次的值,再把他们传替给另外一个函数。
    第二个函数使用传递过来的文件头的文本以及决定新节点的父节点的文件所处的层次来添加新节点。出于第1层次的文件头被添加到树形控件的根节点。处于第二层次的节点被当作处于第一节点的孩子添加到树形控件,等等。根据一个节点是否有子节点,这个函数为每个节点分配一副图。如果一个节点又子节点的话,它获取一副代表一个关闭的文件家的图像。否则,它获取一幅代表一个文件的图像。一个节点使用同一幅显示选中或未选中的状态。
    示例:
    // InitTreeViewItems - extracts headings from the specified file and 
    // passes them to a function that adds them to a tree-view control. 
    // Returns TRUE if successful, or FALSE otherwise. 
    // hwndTV - handle to the tree-view control. 
    // lpszFileName - name of file with headings.BOOL InitTreeViewItems(HWND hwndTV, LPSTR lpszFileName) 

        HANDLE hf;            // handle to file 
        char szItemText[128]; // label text of tree-view item 
        int nLevel;           // heading level     // Open the file to parse. 
        if ((hf = CreateFile(lpszFileName, 
                             GENERIC_READ,
                             FILE_SHARE_READ, 
                             (LPSECURITY_ATTRIBUTES)NULL, 
         OPEN_EXISTING,
                             FILE_ATTRIBUTE_NORMAL,
                             (HANDLE)NULL)) == (HANDLE)INVALID_HANDLE_VALUE) 
             return FALSE;     // Call private function to parse the file looking for headings.
        while (GetNextHeadingAndLevelFromFile(hf, szItemText, &nLevel))         // Add the item to the tree-view control. 
             AddItemToTree(hwndTV, szItemText, nLevel);     return TRUE; 
    } // AddItemToTree - adds items to a tree-view control. 
    // Returns the handle to the newly added item. 
    // hwndTV - handle to the tree-view control. 
    // lpszItem - text of the item to add. 
    // nLevel - level at which to add the item. HTREEITEM AddItemToTree(HWND hwndTV, LPSTR lpszItem, int nLevel)

        TVITEM tvi; 
        TVINSERTSTRUCT tvins; 
        static HTREEITEM hPrev = (HTREEITEM)TVI_FIRST; 
        static HTREEITEM hPrevRootItem = NULL; 
        static HTREEITEM hPrevLev2Item = NULL; 
        HTREEITEM hti;     tvi.mask = TVIF_TEXT | TVIF_IMAGE 
                   | TVIF_SELECTEDIMAGE | TVIF_PARAM;     // Set the text of the item. 
        tvi.pszText = lpszItem; 
        tvi.cchTextMax = sizeof(tvi.pszText)/sizeof(tvi.pszText[0]);     // Assume the item is not a parent item, so give it a 
        // document image. 
        tvi.iImage = g_nDocument; 
        tvi.iSelectedImage = g_nDocument;     // Save the heading level in the item's application-defined 
        // data area. 
        tvi.lParam = (LPARAM)nLevel; 
        tvins.item = tvi; 
        tvins.hInsertAfter = hPrev;     // Set the parent item based on the specified level. 
        if (nLevel == 1) 
            tvins.hParent = TVI_ROOT; 
        else if (nLevel == 2) 
            tvins.hParent = hPrevRootItem; 
        else 
            tvins.hParent = hPrevLev2Item;     // Add the item to the tree-view control. 
        hPrev = (HTREEITEM)SendMessage(hwndTV, 
                                       TVM_INSERTITEM, 
                                       0,
                                       (LPARAM)(LPTVINSERTSTRUCT)&tvins);     // Save the handle to the item. 
        if (nLevel == 1) 
            hPrevRootItem = hPrev; 
        else if (nLevel == 2) 
            hPrevLev2Item = hPrev;     // The new item is a child item. Give the parent item a 
        // closed folder bitmap to indicate it now has child items. 
        if (nLevel > 1)
        { 
            hti = TreeView_GetParent(hwndTV, hPrev); 
            tvi.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE; 
            tvi.hItem = hti; 
            tvi.iImage = g_nClosed; 
            tvi.iSelectedImage = g_nClosed; 
            TreeView_SetItem(hwndTV, &tvi); 
        }     return hPrev; 
    }
      

  4.   

    4. 拖曳一个节点
    拖曳一个节点主要包括处理TVN_BEGINDRAG(或者TVN_BEGINRDRAG)通知消息,WM_MOUSEMOVE消息,以及WM_LBUTTONUP(或WM_RBUTTONUP)消息。也包括使用图像列表的函数,这些函数用来重绘被拖曳的节点图像
    本部分的其余部分提供了一个示例,此示例说明了怎样去拖曳一个树形节点。这个示例由三个函数组成。第一个函数开始拖曳操作,第二个函数拖曳图像,第三个函数结束拖曳操作。
    开始拖曳操作
    树形控件发送父窗口一个TVN_BEGINDRAG通知消息,这个消息表明了用户是否进行了拖曳节点。父窗口以WM_NOTIFY形式接收到一个通知消息,这个的lParam参数就是一个NMTREEVIEW结构的地址。这个结构的成员包括了两个部分,一部分是鼠标的屏幕坐标,另外一部分包括了被拖曳节点的相关信息。
    下面的代码示显示了怎样对WM_NOTIFY进行处理
    case WM_NOTIFY: 
        switch (((LPNMHDR)lParam)->code) 
        {
            case TVN_BEGINDRAG:
                //Main_OnBeginDrag is an application-defined function
                Main_OnBeginDrag(hwndTV, (LPNMTREEVIEW)lParam);
                break;

    开始拖曳操作时,可以使用图像列表的BeginDrag函数,这个函数的参数包括了指向一个图像列表的句柄,这个图像列表包含一幅用来实现拖曳操作的图像以及和该图像的索引 。你可以使用自己定义的图像列表和图像,也可以使用TVM_CREATEDRAGIMAGE消息建立。
    因为在拖曳操作过程中,拖曳的图像代替了鼠标,ImageList_BeginDrag需要你在拖曳图像上指定一个拖曳热点。热点的坐标是相对于拖曳图像的左上方。ImageList_BeginDrag还需要你指定拖曳图像的初始位置。应用程序主要设置初始位置,这样,当用户开始一个拖放操作时,拖曳图像的热点相应的被设置了。
    下面的函数显示了怎样开始拖放一个节点。它使用了树形控件提供的图像并且得到了该节点的矩形边界以确定热点的适合位置。注意矩形边界并没有包含子节点前的空白区域。因为这个原因,该函数添加了热点的x方向的缩进量。
    该函数捕抓了鼠标的输入,这导致所有的鼠标消息都发送给该窗口。父窗口需要WM_MOUSEMOVE序列消息以便决定往何处拖放图像,以及WM_LBUTTONUP消息来确定何时结束拖放操作。
    示例:
    // Main_OnBeginDrag - begins dragging an item in a tree-view control. 
    // hwndTV - handle to the image list. 
    // lpnmtv - address of information about the item being dragged. void Main_OnBeginDrag(HWND hwndTV, LPNMTREEVIEW lpnmtv) { 
        HIMAGELIST himl;    // handle to image list 
        RECT rcItem;        // bounding rectangle of item 
        DWORD dwLevel;      // heading level of item 
        DWORD dwIndent;     // amount that child items are indented 
        // Tell the tree-view control to create an image to use 
        // for dragging. 
        himl = TreeView_CreateDragImage(hwndTV, lpnmtv->itemNew.hItem); 
        // Get the bounding rectangle of the item being dragged. 
        TreeView_GetItemRect(hwndTV, lpnmtv->itemNew.hItem, &rcItem, TRUE); 
        // Get the heading level and the amount that the child items are 
        // indented. 
        dwLevel = lpnmtv->itemNew.lParam; 
        dwIndent = (DWORD)SendMessage(hwndTV, TVM_GETINDENT, 0, 0); 
        // Start the drag operation. 
        ImageList_BeginDrag(himl, 0, 0, 0);
        ImageList_DragEnter(hwndTV, 50, 50);  
        // Hide the mouse pointer, and direct mouse input to the 
        // parent window. 
        ShowCursor(FALSE); 
        SetCapture(GetParent(hwndTV)); 
        g_fDragging = TRUE; 
    拖曳节点
    当父窗口接收到一个WM_MOUSEMOVE消息时,可以通过调用ImageList DragMove函数来实现节点的拖动,就像下列示例所示一样。该示例同样阐述了在拖放过程中进行热点测试,以决定树形控件的节点是否需要高亮显示,以及拖放操作中的目的节点。
    示例:
    // Main_OnMouseMove - drags an item in a tree-view control, 
    // highlighting the item that is the target. 
    // hwndParent - handle to the parent window. 
    // hwndTV - handle to the tree-view control.
    // xCur and yCur - x- and y-coordinates of the mouse pointer. void Main_OnMouseMove(HWND hwndParent, HWND hwndTV, LONG xCur, LONG yCur) 

        HTREEITEM htiTarget;  // handle to target item 
        TVHITTESTINFO tvht;  // hit test information     if (g_fDragging) 
        { 
            // Drag the item to the current position of the mouse pointer. 
            ImageList_DragMove(xCur, yCur);         // Find out if the pointer is on the item. If it is, 
            // highlight the item as a drop target. 
            tvht.pt.x = xCur; 
            tvht.pt.y = yCur; 
            if ((htiTarget = TreeView_HitTest(hwndTV, &tvht)) != NULL) 
            { 
                TreeView_SelectDropTarget(hwndTV, htiTarget); 
            } 
        } 
        return; 

    结束拖放操作
    下列的示例演示了怎样设置和取回树形控件的图象索引。该示例假定在树形控件中只有两种状态图像的索引:未选中和选中的。如果你的程序包括两个以上,这些函数则需要修改才能够处理问题。
    示例
    BOOL TreeView_SetCheckState(HWND hwndTreeView, HTREEITEM hItem, BOOL fCheck){
        TVITEM tvItem;
        tvItem.mask = TVIF_HANDLE | TVIF_STATE;
        tvItem.hItem = hItem;
        tvItem.stateMask = TVIS_STATEIMAGEMASK;
        // Image 1 in the tree-view check box image list is the
        // unchecked box. Image 2 is the checked box.
        tvItem.state = INDEXTOSTATEIMAGEMASK((fCheck ? 2 : 1));
        return TreeView_SetItem(hwndTreeView, &tvItem);
    }
    下面的示例阐述了怎样取回节点的选中状态。
    BOOL TreeView_GetCheckState(HWND hwndTreeView, HTREEITEM hItem)
    {
        TVITEM tvItem;    // Prepare to receive the desired information.
        tvItem.mask = TVIF_HANDLE | TVIF_STATE;
        tvItem.hItem = hItem;
        tvItem.stateMask = TVIS_STATEIMAGEMASK;    // Request the information.
        TreeView_GetItem(hwndTreeView, &tvItem);    // Return zero if it's not checked, or nonzero otherwise.
        return ((BOOL)(tvItem.state >> 12) -1);
    }

      

  5.   

    嗯,能够自己学习、学会并拿出来分享是非常值得赞赏的,很多提问者往往就是懒得查一下MSDN,或者英文基础太差。可惜没有奖励措施,置顶一周让大家学习一下。
      

  6.   

    IDB_OPEN_FILE  这些东西,你是怎么声明的?
      

  7.   

    GetNextHeadingAndLevelFromFile 这个函数是怎么做的/