#if !defined(AFX_DRAGTREECTRL_H__7C20B997_728D_4B6A_AA3C_B2703049185E__INCLUDED_) #define AFX_DRAGTREECTRL_H__7C20B997_728D_4B6A_AA3C_B2703049185E__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 // DragTreeCtrl.h : header file // #define DRAG_DELAY 60 // 60毫秒 / // CDragTreeCtrl window class CDragTreeCtrl : public CTreeCtrl { // Construction public: CDragTreeCtrl(); // Attributes public: private: HTREEITEM m_hItemDragS; // 被拖动的节点 HTREEITEM m_hItemDragD; // 用于放置的节点 HTREEITEM m_hItemDragP; // 放置节点的父节点 DWORD m_dwDragStart; // 开始拖动的时刻 CImageList* m_pDragImage; // 存储拖动过程中产生的图像 BOOL m_bDragging; // 拖动动作状态 UINT m_nScrollTimerID; // 定时检查是否需要滚动 UINT m_nHoverTimerID; // 定时检查鼠标在某节点停留时间,决定是否展开 CPoint m_HoverPoint; // 鼠标停留时所在位置 UINT m_TimerTicks; // Operations public: private: HTREEITEM CopyBranch(HTREEITEM htiBranch,HTREEITEM htiNewParent,HTREEITEM htiAfter); HTREEITEM CopyItem(HTREEITEM hItem,HTREEITEM htiNewParent,HTREEITEM htiAfter); // Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CDragTreeCtrl) //}}AFX_VIRTUAL // Implementation public: virtual ~CDragTreeCtrl(); // Generated message map functions protected: //{{AFX_MSG(CDragTreeCtrl) afx_msg void OnBegindrag(NMHDR* pNMHDR, LRESULT* pResult); afx_msg void OnLButtonDown(UINT nFlags, CPoint point); afx_msg void OnMouseMove(UINT nFlags, CPoint point); afx_msg void OnLButtonUp(UINT nFlags, CPoint point); afx_msg void OnTimer(UINT nIDEvent); //}}AFX_MSG DECLARE_MESSAGE_MAP() }; / //{{AFX_INSERT_LOCATION}} // Microsoft Visual C++ will insert additional declarations immediately before the previous line. #endif // !defined(AFX_DRAGTREECTRL_H__7C20B997_728D_4B6A_AA3C_B2703049185E__INCLUDED_) // DragTreeCtrl.cpp : implementation file // #include "stdafx.h" #include "DlgBase.h" #include "DragTreeCtrl.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif / // CDragTreeCtrl CDragTreeCtrl::CDragTreeCtrl() { m_bDragging = false; } CDragTreeCtrl::~CDragTreeCtrl() { } BEGIN_MESSAGE_MAP(CDragTreeCtrl, CTreeCtrl) //{{AFX_MSG_MAP(CDragTreeCtrl) ON_NOTIFY_REFLECT(TVN_BEGINDRAG, OnBegindrag) ON_WM_LBUTTONDOWN() ON_WM_MOUSEMOVE() ON_WM_LBUTTONUP() ON_WM_TIMER() //}}AFX_MSG_MAP END_MESSAGE_MAP() / // CDragTreeCtrl message handlers void CDragTreeCtrl::OnLButtonDown(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default m_dwDragStart = GetTickCount(); // 记录开始拖动的时刻 CTreeCtrl::OnLButtonDown(nFlags, point); } void CDragTreeCtrl::OnBegindrag(NMHDR* pNMHDR, LRESULT* pResult) { NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR; // TODO: Add your control notification handler code here // 拖动持续时间小于60毫秒视为无意拖动,不处理 if( (GetTickCount() - m_dwDragStart) < DRAG_DELAY ) return; // pNMHDR中存储正在拖动的节点 m_hItemDragS = pNMTreeView->itemNew.hItem; m_hItemDragD = NULL; m_hItemDragP = GetParentItem(m_hItemDragS); // 分配一个CImageList,并加载被拖动图像到其中 m_pDragImage = CreateDragImage( m_hItemDragS ); if( !m_pDragImage ) return; m_bDragging = true; // 开始绘制图像,0表示此图像是Imagelist中的第一幅图像, // CPoint(0,0)表示拖动产生的图像与节点的相对位置 m_pDragImage->BeginDrag ( 0,CPoint(0,0) ); // 在pt位置显示图像,同时禁止拖动过程中控件重绘 CPoint pt = pNMTreeView->ptDrag; ClientToScreen( &pt ); m_pDragImage->DragEnter ( this,pt ); // 拖动过程中,控件始终占据鼠标,防止鼠标移出控件后 // 控件不再处理mousemove/buttonup等消息 SetCapture(); m_nScrollTimerID = SetTimer( 2,40,NULL ); *pResult = 0; } void CDragTreeCtrl::OnMouseMove(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default HTREEITEM hItem; UINT flags; // 鼠标移动过程中,检测鼠标停留的计时器需要不断重置 if( m_nHoverTimerID ) { KillTimer( m_nHoverTimerID ); m_nHoverTimerID = 0; } // 鼠标停留时间设为0.8秒,之后节点自动展开 m_nHoverTimerID = SetTimer( 1,800,NULL ); m_HoverPoint = point; if( m_bDragging ) { // 图像移动到当前pt位置 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 CDragTreeCtrl::OnLButtonUp(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default if( m_bDragging ) { m_bDragging = FALSE; // 隐藏拖动图像,允许控件重绘 CImageList::DragLeave( this ); // 结束一个拖动动作 CImageList::EndDrag(); ReleaseCapture(); // 释放拖动时产生的CImageList, 见MSDN,需要delete delete m_pDragImage; // 不再高亮显示最终停留的节点 SelectDropTarget( NULL ); if( m_hItemDragS == m_hItemDragD || GetParentItem(m_hItemDragD) != m_hItemDragP) { 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_hItemDragP,m_hItemDragD ); DeleteItem( htiNewTemp ); SelectItem( htiNew ); KillTimer( m_nScrollTimerID ); return; } } HTREEITEM htiNew = CopyBranch( m_hItemDragS,m_hItemDragP,m_hItemDragD); DeleteItem( m_hItemDragS ); SelectItem( htiNew ); KillTimer( m_nScrollTimerID ); } CTreeCtrl::OnLButtonUp(nFlags, point); } void CDragTreeCtrl::OnTimer(UINT nIDEvent) { // TODO: Add your message handler code here and/or call default 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); } HTREEITEM CDragTreeCtrl::CopyItem(HTREEITEM hItem, HTREEITEM htiNewParent, HTREEITEM htiAfter) { TV_INSERTSTRUCT tvstruct; HTREEITEM hNewItem; CString strText; // 组织节点信息 tvstruct.item.hItem = hItem; tvstruct.item.mask=TVIF_CHILDREN|TVIF_HANDLE|TVIF_IMAGE|TVIF_SELECTEDIMAGE; GetItem( &tvstruct.item ); strText = GetItemText( hItem ); tvstruct.item.cchTextMax = strText.GetLength (); tvstruct.item.pszText = strText.LockBuffer (); tvstruct.hParent = htiNewParent; tvstruct.hInsertAfter = htiAfter; tvstruct.item.mask = TVIF_IMAGE|TVIF_SELECTEDIMAGE|TVIF_TEXT; // 插入新节点 hNewItem = InsertItem( &tvstruct ); strText.ReleaseBuffer (); return hNewItem; } HTREEITEM CDragTreeCtrl::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; }