如何拖动树视图项

本主题演示了处理拖放树视图项目的代码。 示例代码由三个函数组成。 第一个函数开始拖动操作,第二个函数拖动图像,第三个函数结束拖动操作。

注意

拖动树视图项通常涉及处理 TVN_BEGINDRAG(或 TVN_BEGINRDRAG) 通知代码、WM_MOUSEMOVE 消息和 WM_LBUTTONUP(或 WM_RBUTTONUP)消息。 它还涉及使用 Image Lists 函数在拖动时绘制项。

 

需要了解的事项

技术

先决条件

  • C/C++
  • Windows 用户界面编程

说明

步骤 1:开始树视图拖动操作

每当用户开始拖动项时,树视图控件就会向父窗口发送 TVN_BEGINDRAG(或 TVN_BEGINRDRAG)通知代码。 父窗口以 WM_NOTIFY 消息的形式接收通知,其 lParam 参数是 NMTREEVIEW 结构的地址。 此结构的成员包括鼠标指针的屏幕坐标和包含要拖动的项信息的 TVITEM 结构。

以下示例演示如何处理 WM_NOTIFY 消息以获取 TVN_BEGINDRAG

    case WM_NOTIFY: 
        switch (((LPNMHDR)lParam)->code) 
        {
            case TVN_BEGINDRAG:
                Main_OnBeginDrag(((LPNMHDR)lParam)->hwndFrom, (LPNMTREEVIEW)lParam);
                break;
        
            // Handle other cases here. 
        }
        break; 

开始拖动操作涉及使用 ImageList_BeginDrag 函数。 该函数的参数包括图像列表的句柄,其中包含拖动操作和图像索引期间要使用的图像。 可以提供自己的图像列表和图像,也可以使用 TVM_CREATEDRAGIMAGE 消息通过树视图控件创建。

由于拖动图像可替换拖动操作期间的鼠标指针,因此 ImageList_BeginDrag 需要指定图像中的热点。 热点的坐标相对于图像的左上角。 ImageList_BeginDrag 还需要指定拖动图像的初始位置。 应用程序通常设置初始位置,以便拖动图像的热点对应于用户开始拖动操作时鼠标指针的热点。

以下函数演示如何开始拖动树视图项。 它使用树视图控件提供的拖动图像,并获取项的边框,以确定适合热点的点。 边框的尺寸与图像的尺寸相同。

该函数捕获鼠标输入,导致鼠标消息发送到父窗口。 父窗口需要后续 WM_MOUSEMOVE 消息来确定拖动图像的位置,并需要 WM_LBUTTONUP 消息来确定何时结束拖动操作。

// Begin dragging an item in a tree-view control. 
// hwndTV - handle to the image list. 
// lpnmtv - address of information about the item being dragged.
//
// g_fDragging -- global BOOL that specifies whether dragging is underway.

void Main_OnBeginDrag(HWND hwndTV, LPNMTREEVIEW lpnmtv)
{ 
    HIMAGELIST himl;    // handle to image list 
    RECT rcItem;        // bounding rectangle of item 

    // 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); 

    // Start the drag operation. 
    ImageList_BeginDrag(himl, 0, 0, 0);
    ImageList_DragEnter(hwndTV, lpnmtv->ptDrag.x, lpnmtv->ptDrag.x); 

    // Hide the mouse pointer, and direct mouse input to the 
    // parent window. 
    ShowCursor(FALSE); 
    SetCapture(GetParent(hwndTV)); 
    g_fDragging = TRUE; 

    return; 

} 

步骤 2:拖动树视图项

在父窗口收到 WM_MOUSEMOVE 消息时,通过调用 ImageList_DragMove 函数来拖动树视图项,如以下示例所示。 该示例还演示如何在拖动操作期间执行命中测试,以确定是否将树视图中的其他项目突出显示为拖放操作的目标。

// Drag 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 - coordinates of the mouse pointer,
//     relative to the parent window. 
//
// g_fDragging - global BOOL that specifies whether dragging is underway.

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. 
       // First convert the dialog coordinates to control coordinates. 
       POINT point;
       point.x = xCur;
       point.y = yCur;
       ClientToScreen(hwndParent, &point);
       ScreenToClient(hwndTV, &point);
       ImageList_DragMove(point.x, point.y);
       // Turn off the dragged image so the background can be refreshed.
       ImageList_DragShowNolock(FALSE); 
                
        // Find out if the pointer is on the item. If it is, 
        // highlight the item as a drop target. 
        tvht.pt.x = point.x; 
        tvht.pt.y = point.y; 
        if ((htiTarget = TreeView_HitTest(hwndTV, &tvht)) != NULL) 
        { 
            TreeView_SelectDropTarget(hwndTV, htiTarget); 
        } 
        ImageList_DragShowNolock(TRUE);
    } 
    return; 
}

步骤 3:结束树视图拖动操作

下面的示例演示如何结束拖动操作。 父窗口收到 WM_LBUTTONUP 消息时调用 ImageList_EndDrag 函数。 树视图控件的句柄将传递给函数。

// Stops dragging a tree-view item, releases the 
// mouse capture, and shows the mouse pointer.
//
// g_fDragging - global BOOL that specifies whether dragging is underway.

void Main_OnLButtonUp(HWND hwndTV) 
{ 
    if (g_fDragging) 
    { 
        // Get destination item.
        HTREEITEM htiDest = TreeView_GetDropHilight(hwndTV);
        if (htiDest != NULL)
        {
            // To do: handle the actual moving of the dragged node.
        }
        ImageList_EndDrag(); 
        TreeView_SelectDropTarget(hwndTV, NULL);
        ReleaseCapture(); 
        ShowCursor(TRUE); 
        g_fDragging = FALSE; 
    } 
    return; 
} 

使用树视图控件

CustDTv 示例演示树状视图控件中的自定义绘图