科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道基础软件VC程序中树型控件节点拖动的完美实现

VC程序中树型控件节点拖动的完美实现

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

本实例通过从 CTreeCtrl 中派生了一个类 CXTreeCtrl ,实现树型控件中节点的拖动。

作者:刘涛 来源:天极开发 2007年10月16日

关键字:

  • 评论
  • 分享微博
  • 分享邮件
三、程序代码

// XTreeCtrl.h : header file
#if !defined(AFX_XTREECTRL_H__3EF12526_EF66_4FD9_A572_59476441D79A__INCLUDED_)
#define AFX_XTREECTRL_H__3EF12526_EF66_4FD9_A572_59476441D79A__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
class CXTreeCtrl : public CTreeCtrl
{
 // Construction
 public:
  CXTreeCtrl();
  // Attributes
 public:
  // Operations
 public:
  // Overrides
  // ClassWizard generated virtual function overrides
  //{{AFX_VIRTUAL(CXTreeCtrl)
  //}}AFX_VIRTUAL
  // Implementation
 public:
  virtual ~CXTreeCtrl();
  // Generated message map functions
 protected:
  UINT m_TimerTicks; //处理滚动的定时器所经过的时间
  UINT m_nScrollTimerID; //处理滚动的定时器
  CPoint m_HoverPoint; //鼠标位置
  UINT m_nHoverTimerID; //鼠标敏感定时器
  DWORD m_dwDragStart; //按下鼠标左键那一刻的时间
  BOOL m_bDragging; //标识是否正在拖动过程中
  CImageList* m_pDragImage; //拖动时显示的图象列表
  HTREEITEM m_hItemDragS; //被拖动的标签
  HTREEITEM m_hItemDragD; //接受拖动的标签
  //{{AFX_MSG(CXTreeCtrl)
   afx_msg void OnBegindrag(NMHDR* pNMHDR, LRESULT* pResult);
   afx_msg void OnMouseMove(UINT nFlags, CPoint point);
   afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
   afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
   afx_msg void OnTimer(UINT nIDEvent);
  //}}AFX_MSG
  DECLARE_MESSAGE_MAP()
 private:
  HTREEITEM CopyBranch(HTREEITEM htiBranch,HTREEITEM htiNewParent,HTREEITEM htiAfter);
  HTREEITEM CopyItem(HTREEITEM hItem,HTREEITEM htiNewParent,HTREEITEM htiAfter);
};
#endif
//////////////////////////////////////////////////////////// CXTreeCtrl
#include "stdafx.h"
#include "DragTree.h"
#include "XTreeCtrl.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#define DRAG_DELAY 60
CXTreeCtrl::CXTreeCtrl()
{
 m_bDragging = false;
}
CXTreeCtrl::~CXTreeCtrl()
{}

BEGIN_MESSAGE_MAP(CXTreeCtrl, CTreeCtrl)
//{{AFX_MSG_MAP(CXTreeCtrl)
 ON_NOTIFY_REFLECT(TVN_BEGINDRAG, OnBegindrag)
 ON_WM_MOUSEMOVE()
 ON_WM_LBUTTONUP()
 ON_WM_LBUTTONDOWN()
 ON_WM_TIMER()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

void CXTreeCtrl::OnBegindrag(NMHDR* pNMHDR, LRESULT* pResult)
{
 NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
 *pResult = 0;
 //如果是无意拖曳,则放弃操作
 if( (GetTickCount() - m_dwDragStart) < DRAG_DELAY )
  return;
 m_hItemDragS = pNMTreeView->itemNew.hItem;
 m_hItemDragD = NULL;
 //得到用于拖动时显示的图象列表
 m_pDragImage = CreateDragImage( m_hItemDragS );
 if( !m_pDragImage )
  return;
 m_bDragging = true;
 m_pDragImage->BeginDrag ( 0,CPoint(8,8) );
 CPoint pt = pNMTreeView->ptDrag;
 ClientToScreen( &pt );
 m_pDragImage->DragEnter ( this,pt ); //"this"将拖曳动作限制在该窗口
 SetCapture();
 m_nScrollTimerID = SetTimer( 2,40,NULL );
}

void CXTreeCtrl::OnMouseMove(UINT nFlags, CPoint point)
{
 HTREEITEM hItem;
 UINT flags;
 //检测鼠标敏感定时器是否存在,如果存在则删除,删除后再定时
 if( m_nHoverTimerID )
 {
  KillTimer( m_nHoverTimerID );
  m_nHoverTimerID = 0;
 }
 m_nHoverTimerID = SetTimer( 1,800,NULL ); //定时为 0.8 秒则自动展开
 m_HoverPoint = point;
 if( m_bDragging )
 {
  CPoint pt = point;
  CImageList::DragMove( pt );
  //鼠标经过时高亮显示
  CImageList::DragShowNolock( false ); //避免鼠标经过时留下难看的痕迹
  if( (hItem = HitTest(point,&flags)) != NULL )
  {
   SelectDropTarget( hItem );
   m_hItemDragD = hItem;
  }
  CImageList::DragShowNolock( true );
  //当条目被拖曳到左边缘时,将条目放在根下
  CRect rect;
  GetClientRect( &rect );
  if( point.x < rect.left + 20 )
   m_hItemDragD = NULL;
 }
 CTreeCtrl::OnMouseMove(nFlags, point);
}

void CXTreeCtrl::OnLButtonUp(UINT nFlags, CPoint point)
{
 CTreeCtrl::OnLButtonUp(nFlags, point);
 if( m_bDragging )
 {
  m_bDragging = FALSE;
  CImageList::DragLeave( this );
  CImageList::EndDrag();
  ReleaseCapture();
  delete m_pDragImage;
  SelectDropTarget( NULL );
  if( m_hItemDragS == m_hItemDragD )
  {
   KillTimer( m_nScrollTimerID );
   return;
  }
  Expand( m_hItemDragD,TVE_EXPAND );
  HTREEITEM htiParent = m_hItemDragD;
  while( (htiParent = GetParentItem(htiParent)) != NULL )
  {
   if( htiParent == m_hItemDragS )
   {
    HTREEITEM htiNewTemp = CopyBranch( m_hItemDragS,NULL,TVI_LAST );
    HTREEITEM htiNew = CopyBranch( htiNewTemp,m_hItemDragD,TVI_LAST );
    DeleteItem( htiNewTemp );
    SelectItem( htiNew );
    KillTimer( m_nScrollTimerID );
    return;
   }
  }
  HTREEITEM htiNew = CopyBranch( m_hItemDragS,m_hItemDragD,TVI_LAST );
  DeleteItem( m_hItemDragS );
  SelectItem( htiNew );
  KillTimer( m_nScrollTimerID );
 }
}

HTREEITEM CXTreeCtrl::CopyItem(HTREEITEM hItem, HTREEITEM htiNewParent, HTREEITEM htiAfter) //拷贝条目
{
 TV_INSERTSTRUCT tvstruct;
 HTREEITEM hNewItem;
 CString sText;
 //得到源条目的信息
 tvstruct.item.hItem = hItem;
 tvstruct.item.mask=TVIF_CHILDREN|TVIF_HANDLE|TVIF_IMAGE|TVIF_SELECTEDIMAGE;
 GetItem( &tvstruct.item );
 sText = GetItemText( hItem );
 tvstruct.item.cchTextMax = sText.GetLength ();
 tvstruct.item.pszText = sText.LockBuffer ();
 //将条目插入到合适的位置
 tvstruct.hParent = htiNewParent;
 tvstruct.hInsertAfter = htiAfter;
 tvstruct.item.mask = TVIF_IMAGE|TVIF_SELECTEDIMAGE|TVIF_TEXT;
 hNewItem = InsertItem( &tvstruct );
 sText.ReleaseBuffer ();
 //限制拷贝条目数据和条目状态
 SetItemData( hNewItem,GetItemData(hItem) );
 SetItemState( hNewItem,GetItemState(hItem,TVIS_STATEIMAGEMASK),TVIS_STATEIMAGEMASK);
 return hNewItem;
}

HTREEITEM CXTreeCtrl::CopyBranch(HTREEITEM htiBranch, HTREEITEM htiNewParent, HTREEITEM htiAfter) //拷贝分支
{
 HTREEITEM hChild;
 HTREEITEM hNewItem = CopyItem( htiBranch,htiNewParent,htiAfter );
 hChild = GetChildItem( htiBranch );
 while( hChild != NULL )
 {
  CopyBranch( hChild,hNewItem,htiAfter );
  hChild = GetNextSiblingItem( hChild );
 }
 return hNewItem;
}

void CXTreeCtrl::OnLButtonDown(UINT nFlags, CPoint point) //处理无意拖曳
{
 m_dwDragStart = GetTickCount();
 CTreeCtrl::OnLButtonDown(nFlags, point);
}

void CXTreeCtrl::OnTimer(UINT nIDEvent)
{
 //鼠标敏感节点
 if( nIDEvent == m_nHoverTimerID )
 {
  KillTimer( m_nHoverTimerID );
  m_nHoverTimerID = 0;
  HTREEITEM trItem = 0;
  UINT uFlag = 0;
  trItem = HitTest( m_HoverPoint,&uFlag );
  if( trItem && m_bDragging )
  {
   SelectItem( trItem );
   Expand( trItem,TVE_EXPAND );
  }
 }
 //处理拖曳过程中的滚动问题
 else if( nIDEvent == m_nScrollTimerID )
 {
  m_TimerTicks++;
  CPoint pt;
  GetCursorPos( &pt );
  CRect rect;
  GetClientRect( &rect );
  ClientToScreen( &rect );
  HTREEITEM hItem = GetFirstVisibleItem();
  if( pt.y < rect.top +10 )
  {
   //向上滚动
   int slowscroll = 6 - (rect.top + 10 - pt.y )/20;
   if( 0 == (m_TimerTicks % ((slowscroll > 0) ? slowscroll : 1)) )
   {
    CImageList::DragShowNolock ( false );
    SendMessage( WM_VSCROLL,SB_LINEUP );
    SelectDropTarget( hItem );
    m_hItemDragD = hItem;
    CImageList::DragShowNolock ( true );
   }
  }
  else if( pt.y > rect.bottom - 10 )
  {
   //向下滚动
   int slowscroll = 6 - (pt.y - rect.bottom + 10)/20;
   if( 0 == (m_TimerTicks % ((slowscroll > 0) ? slowscroll : 1)) )
   {
    CImageList::DragShowNolock ( false );
    SendMessage( WM_VSCROLL,SB_LINEDOWN );
    int nCount = GetVisibleCount();
    for( int i=0 ; i<nCount-1 ; i++ )
     hItem = GetNextVisibleItem( hItem );
     if( hItem )
      SelectDropTarget( hItem );
     m_hItemDragD = hItem;
     CImageList::DragShowNolock ( true );
   }
  }
 }
 else
  CTreeCtrl::OnTimer(nIDEvent);
}

////////////////////////////////////////////////////////////
BOOL CDragTreeDlg::OnInitDialog()
{
 CDialog::OnInitDialog();
 …………………….//此处代码省略
 // TODO: Add extra initialization here
 m_image.Create ( IDB_TREEIMAGE,16,1,RGB(255,255,255) );
 m_wndTree.SetImageList ( &m_image,TVSIL_NORMAL );
 HTREEITEM hti1 = m_wndTree.InsertItem ( _T("唐詩"),0,1 );
 HTREEITEM hti2 = m_wndTree.InsertItem ( _T("宋詞"),0,1 );
 HTREEITEM hti3 = m_wndTree.InsertItem ( _T("元曲"),0,1 );
 HTREEITEM hti4 = m_wndTree.InsertItem ( _T("李白"),0,1,hti1 );
 m_wndTree.InsertItem ( _T("靜夜思(床前明月光)"),0,1,hti4 );
 m_wndTree.InsertItem ( _T("將進酒(君不見黃河之水天上來)"),0,1,hti4 );
 m_wndTree.InsertItem ( _T("望廬山瀑布(日照香爐生紫煙)"),0,1,hti4 );
 m_wndTree.InsertItem ( _T("蜀道難(噫吁戲,危乎高哉)"),0,1,hti4 );
 HTREEITEM hti5 = m_wndTree.InsertItem ( _T("杜甫"),0,1,hti1 );
 m_wndTree.InsertItem ( _T("蜀相(丞相祠堂何處尋)"),0,1,hti5 );
 m_wndTree.InsertItem ( _T("春望(國破山河在)"),0,1,hti5 );
 m_wndTree.InsertItem ( _T("茅屋為秋風所破歌(八月秋高風怒號)"),0,1,hti5 );
 HTREEITEM hti6 = m_wndTree.InsertItem ( _T("白居易"),0,1,hti1 );
 m_wndTree.InsertItem ( _T("長恨歌(漢皇重色思傾國)"),0,1,hti6 );
 m_wndTree.InsertItem ( _T("琵琶行並序(潯陽江頭夜送客)"),0,1,hti6 );
 m_wndTree.InsertItem ( _T("李清照"),0,1,hti2 );
 m_wndTree.InsertItem ( _T("柳永"),0,1,hti2 );
 return TRUE; // return TRUE unless you set the focus to a control
}

  四、小结

  本实例介绍了树型控件如何实现项目条的拖动,它的实现思路主要是利用了定时器,不同的定时器来实现不同的功能,另外,读者朋友在学习中要主要掌握CimageList类的几个成员函数的用法。

查看本文来源

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章