Drag & Drop & Background Image Tree Control

发表于:2007-07-01来源:作者:点击数: 标签:
简介 此文章介绍了使用我 开发 的cTree类来完成树控件中条目的拖拽和为树控件设置图片(bmp)背景的功能。示例程序中只允许非主节点中条目的拖拽。 代码细节 所有的实现过程都在文件cTree.cpp和cTree.h中. 拖拽实现主要在OnBeginDrag()和OnLButtonUp()函数中


简介

此文章介绍了使用我开发的cTree类来完成树控件中条目的拖拽和为树控件设置图片(bmp)背景的功能。示例程序中只允许非主节点中条目的拖拽。

代码细节
所有的实现过程都在文件cTree.cpp和cTree.h中.

拖拽实现主要在OnBeginDrag()和OnLButtonUp()函数中完成. 在OnBeginDrag函数中,我创建了一个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();

}

在NM_TREEVIEW结构创建后,所做的第一件事情就是判断当前是否是根节点,如果是,就返回。当然如果需要的话,你可以注释掉这一行,使得根节点也可以拖拽。

然后我把选中的条目保存在我的HTREEITEM型成员变量var m_itemDrag中 。在拖拽开始时,m_itemDrop值为NULL。CreateDragImage() 函数返回一个CImageList类型的指针,这个指针保存在变量m_pDragImage中。

接下来在拖拽开始时设置布尔值m_bLDraggingm为真,下面是鼠标拖拽子条目时的代码:

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确定当前是否是拖拽的条目。如果是,我们取得当前鼠标的位置并把当天拖拽的条目移动到此位置。

接下来我们调用HitTest()函数,判断当前鼠标的位置是否在一个条目的上方。如果不是,传递NULL,如果是,传递该条目的HTREEITEM结构。
然后我们通过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()函数和SetFont()函数创建我们需要的树控件。然后我们创建一个bitmap,这样我们可以加载与图片列表相关联的资源,用于树控件的图片列表。然后我们把它传递给TreeCtrl对象的SetImageList()方法。

我们也可以在cTree的构造函数中使用函数SetDefaultCursor(),这样在创建控件时自动加载锁型鼠标和箭头鼠标。

其他一些有用的函数
AddGroup(CString groupname) - 为树控件加入一个根节点
DeleteGroup(CString groupname) - 输出一个根节点
AddChild(CString childname,CString groupname) - 为指定的根节点(组)加入子条目
DeleteChild(CString childname,CString groupname) - 为指定的根节点(组)删除子条目
GetChildCountInGroup(CString groupname) - 得到一个根节点(组)中子条目的个数
SetBkImage(UINT) - 通过资源ID设置树控件背景图片
SetBkImage(LPCTSTR) - 通过加载字符串设置树控件背景图片

原文转自:http://www.ltesting.net