树形控件拖动效果的实现,父节点不能拖到子节点里

class CDBLayerTree : public CTreeCtrl
CDBLayerTree派生于CTreeCtrl类
CTreeCtrl派生于CWnd类,树形结构其实是一个窗口

添加消息响应函数:

afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
afx_msg void OnBegindrag(NMHDR* pNMHDR, LRESULT* pResult);
afx_msg void OnTimer(UINT_PTR nIDEvent);
DECLARE_MESSAGE_MAP()

添加函数:

HTREEITEM CopyBranch(HTREEITEM htiBranch, HTREEITEM htiNewParent, HTREEITEM htiAfter);
HTREEITEM CopyItem(HTREEITEM hItem, HTREEITEM htiNewParent, HTREEITEM htiAfter);

添加变量:

protected://HPD20161103
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; //接受拖动的标签
BOOL m_bIsChange;

函数的实现:
cpp文件加入宏:

#define   DRAG_DELAY   60
构造函数内:
m_bDragging = false;
void CDBLayerTree::OnLButtonDown(UINT nFlags, CPoint point)
{
    // TODO: 在此添加消息处理程序代码和/或调用默认值
    //处理无意拖曳
    m_dwDragStart = GetTickCount();
    CTreeCtrl::OnLButtonDown(nFlags, point);
}
void CDBLayerTree::OnLButtonUp(UINT nFlags, CPoint point)
{
    //TODO:在此添加消息处理程序代码和/或调用默认值
    CString strF =_T(""), strT =_T("");
    __super::OnLButtonUp(nFlags, point);
    //__super是基类的意思
    
    if( m_bDragging )
    {
        m_bDragging = FALSE;
        CImageList::DragLeave( this );
        CImageList::EndDrag();
        ReleaseCapture();
        delete m_pDragImage;

        SelectDropTarget( NULL );

        if( m_hItemDragS == m_hItemDragD )
        {           
            goto label;
        }

        HTREEITEM hParentDragS = GetParentItem(m_hItemDragS);
        HTREEITEM hParentDragD = GetParentItem(m_hItemDragD);

        if(hParentDragS != hParentDragD)
        {
            goto label;
        }

        long lLayerHandleS,lLayerHandleD;
        lLayerHandleS = GetItemData(m_hItemDragS);
        lLayerHandleD = GetItemData(m_hItemDragD);

        {
            long  InitialPosition = -1;
            long  TargetPosition = -1;
            long  nCount = -1;
            HTREEITEM hNextItem;
            HTREEITEM hChildItem = GetChildItem(hParentDragS);

            while (hChildItem != NULL)
            {
                nCount++;
                hNextItem = GetNextItem(hChildItem, TVGN_NEXT);
                if(hChildItem == m_hItemDragS)
                {
                    InitialPosition = nCount;
                    break;
                }
                hChildItem = hNextItem;
            }

            nCount = -1;
            hChildItem = GetChildItem(hParentDragS);

            while (hChildItem != NULL)
            {
                nCount++;
                hNextItem = GetNextItem(hChildItem, TVGN_NEXT);
                if(hChildItem == m_hItemDragD)
                {
                    TargetPosition = nCount;
                    HTREEITEM  htiNew;
                    if( InitialPosition < TargetPosition )  //被拖动的往下拖,则显示在接受拖动的下面
                    {
                        htiNew = CopyBranch( m_hItemDragS, GetParentItem(m_hItemDragD), m_hItemDragD );
                    }
                    else         //被拖动的往上拖,则显示在接受拖动的上面
                    {
                        if( TargetPosition == 0 )  
                        {
                            htiNew = CopyBranch( m_hItemDragS, GetParentItem(m_hItemDragD), TVI_FIRST );
                        }
                        else
                        {
                            htiNew = CopyBranch( m_hItemDragS, GetParentItem(m_hItemDragD), GetPrevSiblingItem(m_hItemDragD) );
                        }
                    }

                    DeleteItem( m_hItemDragS );
                    SelectItem( htiNew );
                    KillTimer( m_nScrollTimerID );
                    return;
                }   
                hChildItem = hNextItem;
            }
        }
label:
        KillTimer( m_nScrollTimerID );
        m_hItemDragS = NULL;
        m_hItemDragD = NULL;
    }

    //CTreeCtrl::OnLButtonUp(nFlags, point);
}
void CDBLayerTree::OnMouseMove(UINT nFlags, CPoint point)
{
    // TODO: 在此添加消息处理程序代码和/或调用默认值
    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;

        __super::OnMouseMove(nFlags, point);
    }

    return;
}

//开始拖动的消息响应

void CDBLayerTree::OnBegindrag(NMHDR* pNMHDR, LRESULT* pResult) 
{
    NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
    *pResult = 0;

    //如果是无意拖曳,则放弃操作
    if( (GetTickCount() - m_dwDragStart) < DRAG_DELAY )
    {
        smartlog<<"(GetTickCount() - m_dwDragStart) < DRAG_DELAY!";
        return;
    }
    HTREEITEM item = pNMTreeView->itemNew.hItem;

    HTREEITEM hParent = GetParentItem(item);

    if( !(hParent != NULL &&  GetParentItem(hParent) == NULL) )
    {
        smartlog<<"hParent != NULL &&  GetParentItem(hParent) == NULL!";
        return;
    }
    m_hItemDragS = item;
    m_hItemDragD = NULL;

    DWORD_PTR pData = GetItemData(m_hItemDragS);

    if( long(pData) >= 0 )
        m_bDragging = true;

    //得到用于拖动时显示的图象列表
    if( m_bDragging == TRUE )
        m_pDragImage = CreateDragImage( m_hItemDragS );
    else
        m_pDragImage = NULL;

    if( !m_pDragImage )
    {
        smartlog<<"m_pDragImage为NULL!";
        return;
    }
    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 CDBLayerTree::OnTimer(UINT_PTR nIDEvent)
{
    // TODO: 在此添加消息处理程序代码和/或调用默认值
    //鼠标敏感节点
    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
        __super::OnTimer(nIDEvent);
}

//拷贝条目

HTREEITEM CDBLayerTree::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 CDBLayerTree::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;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容