65.9K
CodeProject 正在变化。 阅读更多。
Home

拖放和背景图像树控件

starIconstarIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIcon

2.42/5 (43投票s)

2003年1月15日

4分钟阅读

viewsIcon

181282

downloadIcon

8050

一篇文章,允许轻松拖放和位图背景以及其他树控件功能

Sample Image - dragdroptree.jpg

引言

这是我的第一篇 CodeProject 文章,所以如果有人给我低分,我真的很想知道原因(以便改进)。由于我一直是 CodeProject.com 的忠实读者大约 3-4 年,我决定最终提交我手中许多可能对他人有用的东西之一。

本文详细介绍了如何使用我的 cTree 类,该类允许轻松实现拖放功能,并快速为 TreeCtrl 创建背景图像。我的图片和上面示例图像中的阴影 URL 都是 TreeCtrl 背景的一部分!我无法捕捉真实的“禁止”图标,因为屏幕截图无法捕捉鼠标图标,所以我不得不在 Photoshop 中制作(因此,“禁止”图标异常粗大 :-))。但上面的图像只是为了让你了解我的类能做什么,所以下载它来看看吧!

该类还允许你无需任何操作即可实现拖放,并且它只允许在根节点之间进行拖放(如果你愿意,可以轻松更改此设置)。

代码详情

所有核心内容都位于 cTree.cppcTree.h 中。

拖放是通过 OnBeginDrag()OnLButtonUp() 实现的。在 OnBeginDrag 中,我使用 NMHDR 强制转换为 NM_TREEVIEW 结构

void cTree::OnBeginDrag(NMHDR* pNMHDR, LRESULT* pResult) 
{
    NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
    *pResult = 0;
    
    // So user cant drag root node
    if (GetParentItem(pNMTreeView->itemNew.hItem) == NULL) return ; 

    // Item user started dragging ...
    m_hitemDrag = pNMTreeView->itemNew.hItem;
    m_hitemDrop = NULL;


        // get the image list for dragging
    m_pDragImage = CreateDragImage(m_hitemDrag);  
    // CreateDragImage() returns NULL if no image list
    // associated with the tree view control
    if( !m_pDragImage )
        return;

    m_bLDragging = TRUE;
    m_pDragImage->BeginDrag(0, CPoint(-15,-15));
    POINT pt = pNMTreeView->ptDrag;
    ClientToScreen( &pt );
    m_pDragImage->DragEnter(NULL, pt);
    SetCapture();
}

在将 NMHDR 强制转换为 NM_TREEVIEW 结构后,我首先要做的是测试实际的项是否是根节点。如果是,我直接返回,但如果你希望根节点可以拖动,可以轻松注释掉这一行。

然后,我将拖动的项保存到我的成员变量 m_itemDrag 中,它是一个 HTREEITEMm_itemDropNULL,因为用户刚刚开始拖动该项。CreateDragImage() 返回一个 CImageList 类型的指针(它是与 HTREEITEM 关联的图像的副本),我们将其存储在 m_pDragImage 中。

接下来,我们需要在拖动开始的点绘制图像,并将布尔值 m_bLDragging 设置为 true,这样我们就知道拖动已经开始。接下来的代码处理所有后续的鼠标移动,直到松开左键。

void cTree::OnMouseMove(UINT nFlags, CPoint point) 
{
   HTREEITEM    hitem;
   UINT        flags;

   if (m_bLDragging)
   {
       POINT pt = point;
       ClientToScreen(&pt);
       CImageList::DragMove(pt);
       if ((hitem = HitTest(point, &flags)) != NULL)
       {
           CImageList::DragShowNolock(FALSE);
                
           // Tests if dragged item is over another child !
           if ((GetParentItem(hitem) != NULL) && (cursor_no != ::GetCursor())) 
           {
                ::SetCursor(cursor_no);
                // Dont want last selected target highlighted after mouse
                // has moved off of it, do we now ?
                SelectDropTarget(NULL);
           }
           // Is item we're over a root node and not parent root node ?
           if ((GetParentItem(hitem) == NULL) && (GetParentItem(m_hitemDrag) != hitem )) 
           {
               if (cursor_arr != ::GetCursor()) ::SetCursor(cursor_arr); 
                   SelectDropTarget(hitem);
           }

           m_hitemDrop = hitem;
           CImageList::DragShowNolock(TRUE);
       }
   }
   else 
   {
       // Set cursor to arrow if not dragged
       // Otherwise, cursor will stay hand or arrow depen. on prev setting
       ::SetCursor(cursor_arr);
   }

   CTreeCtrl::OnMouseMove(nFlags, point);
}

OnMouseMove() 的开头,我们检查布尔值 m_bLDragging 是否已设置为 true,以便我们知道用户已开始拖动某个项。如果确实如此,那么我们获取当前的鼠标位置并将拖动的项移动到该位置。

接下来,我们调用 HitTest() 来查看当前鼠标位置是否位于某个项上方。如果不是,则返回 NULL;否则,它会返回鼠标所在的项的 HTREEITEM。我们调用 DragShowNoLock(FALSE) 来阻止 CImageList 在我们检查当前项状态时进行绘制(实际上是隐藏)。
接下来,我们检查从 HitTest() 返回的项是否不是子项,如果是,我们将光标设置为 cursor_no,以便用户看到光标已更改为不允许的操作。如果该项位于根节点上方,我们将光标设置为箭头,并允许 CImageList 绘制被拖动的项。

CMainFrame 中,你可以看到创建 Treectrl 以及添加和删除组和子项是多么容易。

void CMainFrame::CreateTreeCtrl()
{
    if (tree_ctrl) tree_ctrl.DestroyWindow() ;

    CRect rect;
    GetClientRect(&rect);

    tree_ctrl.Create( /*|| TVS_EDITLABELS |*/ WS_VISIBLE | 
                          WS_TABSTOP | WS_CHILD /*| WS_BORDER | LBS_NOTIFY*/
                          /*| TVS_LINESATROOT | TVS_HASLINES */
                          /*& TVS_NOTOOLTIPS & TVS_HASBUTTONS*/,
        CRect(10,10,rect.right-5,rect.bottom),this,ID_TREELISTBOX);

    CFont listBoxFont ;
        listBoxFont.CreateFont(
        16,                       // nHeight
        0,                        // nWidth
        0,                        // nEscapement
        0,                        // nOrientation
        FW_BOLD,                  // nWeight
        FALSE,                    // bItalic
        FALSE,                    // bUnderline
        0,                        // cStrikeOut
        ANSI_CHARSET,             // nCharSet
        OUT_DEFAULT_PRECIS,       // nOutPrecision
        CLIP_DEFAULT_PRECIS,      // nClipPrecision
        DEFAULT_QUALITY,          // nQuality
        DEFAULT_PITCH | FF_SWISS, // nPitchAndFamily
        "Arial");  

        tree_ctrl.SetFont(&listBoxFont,FALSE);

    COLORREF acolor=RGB(0,0,0);
    tree_ctrl.SetTextColor(acolor);

    CBitmap bmp;

    // normal tree images    
    tree_imageList.Create(IDB_TREE_IMAGELIST,18,7, RGB(255, 0 ,255));    

    ASSERT(tree_imageList.m_hImageList);

    bmp.LoadBitmap(IDB_TREE_IMAGELIST);
    tree_imageList.Add( &bmp, RGB(255,255,255));
    bmp.DeleteObject();
        //TVSIL_STATE
    tree_ctrl.SetImageList(&tree_imageList,TVSIL_NORMAL); 

    tree_ctrl.AddGroup("CodeProject")  ;
    tree_ctrl.AddGroup("DanCclark.com")  ;
    tree_ctrl.AddGroup("Peeps")  ;
    tree_ctrl.AddGroup("Folders")  ;

    tree_ctrl.AddChild("danclark","Other Contacts") ;
    tree_ctrl.AddChild("fugazi","Other Contacts") ;
}

首先,我在 MainFrm.h 中声明了一个 cTree 类型的成员变量 tree_ctrl,然后在 CreateTreeCtrl 中,我们调用 Create 并将我们希望 Treectrl 使用的字体类型传递给 SetFont。然后我们创建一个 bitmap,并将与 imagelist 关联的资源加载到其中,然后将其添加到 tree_imageList 中,最后将其传递给 TreeCtrl 对象 SetImageList()

此外,我在 cTree 的构造函数中放置了 SetDefaultCursor 函数,以便在创建时自动加载“禁止”光标和箭头。

我还决定不介绍绘制 TreeCtrl 背景的函数,因为整篇文章对大多数人来说太长了。

其他有用函数

  • AddGroup(CString groupname) - 向 TreeCtrl 添加组。
  • DeleteGroup(CString groupname) - 删除组。
  • AddChild(CString childname,CString groupname) - 向给定组添加一个子项。
  • DeleteChild(CString childname,CString groupname) - 删除给定组中的一个子项。
  • GetChildCountInGroup(CString groupname) - 返回组中的子项数量。
  • SetBkImage(UINT) - 使用资源 ID 设置 TreeCtrl 的背景图像。
  • SetBkImage(LPCTSTR) - 使用字符串位置设置 TreeCtrl 的背景图像。

许可证

本文未附加明确的许可证,但可能在文章文本或下载文件本身中包含使用条款。如有疑问,请通过下面的讨论区联系作者。

作者可能使用的许可证列表可以在此处找到。

© . All rights reserved.