MFC TreeControl简单应用

对于TreeControl常用操作,做如下介绍。

1. TreeControl添加节点

  1. 在界面种选择TreeControl控件,点击右键,在弹出的菜单种选择【添加变量】,在弹出的界面中输入变量名 "m_list”
  2. 点击界面右键,在弹出的菜单中选择【类向导】,选中“虚函数”选项卡,选择其中的“OnInitDialog”函数,然后点击【添加函数按钮】,然后点击确定。
  3. 将TreeControl控件中的属性“Has Buttons”、”Line at root“和“Has Line”后改为 “True”,如果相实现双击更改其值的功能,则将”Edit Labels“改为”True“
  4. 如果想要在树节点前添加图标,则需要导入*.icon的图标文件。具体方法为:
    选择资源视图,在资源视图中点击右键,在弹出的菜单中选择【添加资源】,在弹出的界面中点击【导入】按钮,然后选择.icon的文件。此图标导入后的名称为”IDI_ICON1“

  5. 在初始化函数中添加节点代码
//在.h文件中添加如下代码
CImageList m_imageList1;
//修改初始化函数
BOOL TreeTest::OnInitDialog()
{
    CDialogEx::OnInitDialog();

    m_list.SetBkColor(RGB(230,230,230));//设置树的颜色

    m_imageList1.Create(16,16,ILC_COLOR8|ILC_MASK,0,4);//第五个参数为图标的总数
    m_imageList1.Add(AfxGetApp()->LoadIconW(IDI_ICON1));//将图标添加到imageList中

    m_list.SetImageList(&m_imageList1,TVSIL_NORMAL);//将图标进行加载

    
    HTREEITEM root = m_list.InsertItem(_T("root"));//插入根节点
    HTREEITEM hChild1 = m_list.InsertItem(_T("child1"),root);//在根节点下插入子节点
    HTREEITEM hChild2 = m_list.InsertItem(_T("child2"),root);//在根节点下插入子节点
    HTREEITEM hChild3 = m_list.InsertItem(_T("child3"),root);//在根节点下插入子节点

    m_list.SetItemImage(root,0,0);//设置根节点图标
    m_list.SetItemImage(hChild1,0,0);
    m_list.SetItemImage(hChild2,0,0);
    m_list.SetItemImage(hChild3,0,0);//设置子节点图标,注意:第一个参数为要设置的节点、第二个参数为默认状态下的图标、第三个值为选中后的图标。


    return TRUE;  // return TRUE unless you set the focus to a control

}

2. TreeControl菜单

  1. 新建一个菜单,内容为”增加“、”删除“、”重命名“
  2. 选中Tree控件,点击右键,在弹出的菜单中选择”类向导“;在弹出的界面中选择【命令】、对象下选择”IDC_TREE1“,在消息中选择右键消息”NM_RCLICK“,然后点击【添加处理程序】按钮。
  3. 选中节点后才可以加载菜单,具体代码如下:
void TreeTest::OnRclickTree1(NMHDR *pNMHDR, LRESULT *pResult)
{
    CPoint ScreenPt;
    GetCursorPos(&ScreenPt);

    CPoint pt =GetCurrentMessage()->pt;//获取当前鼠标点击消息的坐标点
    m_list.ScreenToClient(&pt);//将鼠标的屏幕坐标,转换成树控件的客户区域坐标
    UINT uFlags = 0;
    HTREEITEM hItem = m_list.HitTest(pt,&uFlags);//然后做点击测试

    /**判断右键是否在节点上**/
    if ((hItem != NULL)&&(TVHT_ONITEM & uFlags))//如果点击位置在界面位置上面
    {
        
        CMenu menu;
        menu.LoadMenuW(IDR_MENU1);//装载第一个子菜单,即我们菜单的第一列
        CMenu* pPopup = menu.GetSubMenu(0);
        pPopup->TrackPopupMenu(TPM_LEFTALIGN, ScreenPt.x, ScreenPt.y, this);//弹出菜单 2
    
    }
    *pResult = 0;
}

3. TreeControl修改节点

参考链接
实现双击修改节点名称,双击时速度需要慢一些,快速双击为展开和折叠节点

  1. 属性设置:将”Edit Labels“改为”True“
  2. 选中Tree控件,在弹出的菜单中选择【类向导】,添加消息函数”TVN_BEGINLABELEDIT“ 和 ”TVN_ENDLABELEDIT“
  3. 添加全局变量CString g_sSelectStr
  4. 修改两个消息函数,,如下:
void TreeTest::OnBeginlabeleditTree1(NMHDR *pNMHDR, LRESULT *pResult)
{
    LPNMTVDISPINFO pTVDispInfo = reinterpret_cast<LPNMTVDISPINFO>(pNMHDR);

    g_sSelectStr = m_list.GetItemText(m_list.GetSelectedItem());//得到修改前的数据
    *pResult = 0;
}


void TreeTest::OnEndlabeleditTree1(NMHDR *pNMHDR, LRESULT *pResult)
{
    LPNMTVDISPINFO pTVDispInfo = reinterpret_cast<LPNMTVDISPINFO>(pNMHDR);
    
    CString strName; //修改后的数据
    CString rootstr;
    m_list.GetEditControl()->GetWindowText(strName);
    if (strName.IsEmpty())
    {
        AfxMessageBox(_T("数据项不能为空,请重新输入"));
        return;
    }
    if (strName.Compare(g_sSelectStr) == 0)//名字未做修改
    {
        return;
    }
    //判断是否由重复的名字
    HTREEITEM hRoot = m_list.GetRootItem();
    HTREEITEM hFind = FindItem(hRoot,strName);
    if (hFind == NULL)//如果没有重名,则修改
    {
        CString strText;
        m_list.GetEditControl()->GetWindowText(strText);
        m_list.SetItemText(m_list.GetSelectedItem(),strText);
    }
    *pResult = 0;
}

4. TreeControl查找节点

//根据名称来查找节点
HTREEITEM TreeTest::FindItem(HTREEITEM item,CString strText)
{
    HTREEITEM hFind;
    if (item == NULL)//修改数据与根数据不同,遍历时使用
    {
        return NULL;
    }
    while(item != NULL)
    {
        if (strText.Compare(m_list.GetItemText(item))  == 0)//名字相同
        {
            return item;
        }
        if (m_list.ItemHasChildren(item))//如果有子节点,则继续判断
        {
            item = m_list.GetChildItem(item);
            hFind = FindItem(item,strText);
            if (hFind)
            {
                return hFind;
            }
            else
            {
                item = m_list.GetNextSiblingItem(m_list.GetParentItem(item));
            }
        }
        else
        {
            item = m_list.GetNextSiblingItem(item);
            return FindItem(item,strText);
        }
    }
    return item;
}

5. TreeControl折叠展开节点

//折叠所有的树节点
void TreeTest::mFoldTree(HTREEITEM hTreeItem)
{
    if(!m_list.ItemHasChildren(hTreeItem))    
    {    
        return;    
    }    
    HTREEITEM hNextItem = m_list.GetChildItem(hTreeItem);
    while (hNextItem != NULL)    
    {   
        ExpandTree(hNextItem);    
        hNextItem = m_list.GetNextItem(hNextItem, TVGN_NEXT);    
    }    
    m_list.Expand(hTreeItem,TVE_COLLAPSE); 
}

//展开所有的树节点
void TreeTest::ExpandTree(HTREEITEM hTreeItem)
{
    if(!m_list.ItemHasChildren(hTreeItem))    
    {    
        return;    
    }  
    HTREEITEM hNextItem = m_list.GetChildItem(hTreeItem);
    while (hNextItem != NULL)    
    {   
        ExpandTree(hNextItem); 
        hNextItem = m_list.GetNextItem(hNextItem, TVGN_NEXT);    
    } 
    HTREEITEM hchild = m_list.GetChildItem(hTreeItem);
    CString NodeData,NodeName;
    NodeName = m_list.GetItemText(hchild);
    if (NodeData.Compare(_T("2")) == 0 )
    {
        return;
    }
    m_list.Expand(hTreeItem,TVE_EXPAND); 
}

//展开单独的树节点
void TreeTest::ExpandTree2(HTREEITEM hTreeItem)
{
    if(!m_list.ItemHasChildren(hTreeItem))    
    {    
        return;    
    }  
    HTREEITEM hNextItem = m_list.GetChildItem(hTreeItem);
    while (hNextItem != NULL)    
    {   
        ExpandTree(hNextItem); 
        hNextItem = m_list.GetNextItem(hNextItem, TVGN_NEXT);    
    } 
    m_list.Expand(hTreeItem,TVE_EXPAND); 
}


void TreeTest::OnClickTree1(NMHDR *pNMHDR, LRESULT *pResult)
{
    hTreeItem = m_list.GetSelectedItem();
    *pResult = 0;
}

//折叠树按钮
void TreeTest::OnBnClickedButton1()
{
    HTREEITEM hItem = m_list.GetSelectedItem();
    if (hItem == NULL)
    {
        HTREEITEM hroot = m_list.GetRootItem();
        mFoldTree(hroot);     
    }
    else
    {
        mFoldTree(hItem);
    }
}

//展开树按钮
void TreeTest::OnBnClickedButton7()
{
    HTREEITEM hItem = m_list.GetSelectedItem();
    if (hItem == NULL)
    {
        HTREEITEM hroot = m_list.GetRootItem();
        ExpandTree(hroot);
    }
    else
    {
        ExpandTree2(hItem);
    }
}

6. TreeControl拖动树

参考网址1
参考网址2
参考网址3
参考网址4

创建树的时候,使用继承类TreeControl进行创建。例如:
注意:此方法目前具有一定的局限性,即节点下必须有子节点,才可以完成拖动。后期进行优化

CXtreeCtrl m_list;
HTREEITEM root =    m_list.InsertItem(_T("root"));
HTREEITEM parent1 = m_list.InsertItem(_T("1"),root);
m_list.InsertItem(_T("11"),parent1);

HTREEITEM parent2 = m_list.InsertItem(_T("2"),root);
m_list.InsertItem(_T("21"),parent2);
    
HTREEITEM parent3 = m_list.InsertItem(_T("3"),root);
m_list.InsertItem(_T("31"),parent3);

HTREEITEM parent4 = m_list.InsertItem(_T("4"),root);
m_list.InsertItem(_T("41"),parent4);

7. 继承类TreeControl

XTreeCtrl.cpp

// XtreeCtrl.cpp : implementation file
//

#include "stdafx.h"
#include "Drag.h"
#include "XtreeCtrl.h"
#define DRAG_DELAY 60

// CXtreeCtrl

IMPLEMENT_DYNAMIC(CXtreeCtrl, CTreeCtrl)

CXtreeCtrl::CXtreeCtrl()
{
    m_bDragging = FALSE;
    //m_hSelectItem = NULL;
    m_hItemDragS = NULL;
    m_nHoverTimerID = 0;
    m_nScrollTimerID = 0;
    m_bInsertAbove = FALSE;
    m_hItemDragD = NULL;

}

CXtreeCtrl::~CXtreeCtrl()
{
}


BEGIN_MESSAGE_MAP(CXtreeCtrl, CTreeCtrl)
    ON_NOTIFY_REFLECT(TVN_BEGINDRAG, &CXtreeCtrl::OnTvnBegindrag)
    ON_WM_LBUTTONUP()
    ON_WM_MOUSEMOVE()
    ON_WM_TIMER()
    ON_WM_LBUTTONDOWN()
END_MESSAGE_MAP()



// CXtreeCtrl message handlers



void CXtreeCtrl::OnTvnBegindrag(NMHDR *pNMHDR, LRESULT *pResult)
{
    LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
    // TODO: Add your control notification handler code here
    
    *pResult = 0;
    if( (GetTickCount() - m_dwDragStart) < DRAG_DELAY )
        return;
    
    m_hItemDragS = pNMTreeView->itemNew.hItem;
    if (!ItemHasChildren(m_hItemDragS))             //子节点不能被拖拽,注释后可以随意拖拽
    {
        return;
    }
    m_hItemDragD = NULL;
    m_bDragging = true;
    m_nScrollTimerID = SetTimer( 2,40,NULL );
}
HTREEITEM CXtreeCtrl::GetDragTarget(HTREEITEM hItem, POINT point, BOOL& bInsertAbove)
{
    ASSERT(hItem != NULL);
    HTREEITEM hDragTarget;
    CRect rectItem;
    GetItemRect(hItem, &rectItem, FALSE);
    if (point.y < rectItem.CenterPoint().y)
    {
        hDragTarget = hItem;
        bInsertAbove = TRUE;
    }
    else
    {
        if (ItemHasChildren(hItem) && (GetItemState(hItem, TVIS_EXPANDED) & TVIS_EXPANDED))
        {
            hDragTarget = GetChildItem(hItem);    
            bInsertAbove = TRUE;
        }
        else
        {
            hDragTarget = hItem;
            bInsertAbove = FALSE;
        }
    }
    ASSERT(hDragTarget != NULL);
    return hDragTarget;
}

void CXtreeCtrl::OnMouseMove(UINT nFlags, CPoint point)
{
    // TODO: Add your message handler code here and/or call default

    HTREEITEM hItem;
    UINT      flags;
    if (m_nHoverTimerID > 0)
    {
        KillTimer(m_nHoverTimerID);
        m_nHoverTimerID = 0;
    }   
    if( m_bDragging )
    {
        m_nHoverTimerID = SetTimer(1, 800, NULL);
        m_HoverPoint = point;
        //鼠标经过时高亮显示
        CImageList::DragShowNolock( false ); //避免鼠标经过时留下难看的痕迹
        HTREEITEM m_hNextDragTarget;    
        BOOL m_bNextInsertAbove;        
        if( (hItem = HitTest(point,&flags)) != NULL )
        {
            m_hNextDragTarget = GetDragTarget(hItem, point, m_bNextInsertAbove);
            if ((m_hNextDragTarget != m_hItemDragD) || (m_bInsertAbove != m_bNextInsertAbove))
            {
                SelectDropTarget(m_hNextDragTarget);
                m_hItemDragD = m_hNextDragTarget;
                m_bInsertAbove = m_bNextInsertAbove;
                EnsureVisible(m_hItemDragD);
                RedrawWindow();
            }
        }
        if ((GetScrollPos(SB_HORZ) > 0) && (GetScrollLimit(SB_VERT) > 0))
        {
            Invalidate();
        }
    }
    CTreeCtrl::OnMouseMove(nFlags, point);
}
HTREEITEM CXtreeCtrl::CopyItem(HTREEITEM hItem1, HTREEITEM htiNewParent, HTREEITEM htiAfter) //拷贝条目
{
    TV_ITEM tvSrc;
    tvSrc.mask  = TVIF_HANDLE | TVIF_PARAM | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_STATE;
    tvSrc.hItem = hItem1;
    if (!GetItem(&tvSrc)) 
        return  htiNewParent;

    tvSrc.hItem = htiNewParent;
    SetItem(&tvSrc);
    SetItemText(htiNewParent,GetItemText(hItem1));
    SetCheck(htiNewParent,GetCheck   (hItem1));

    if (tvSrc.state & TVIS_EXPANDED) 
        Expand(htiNewParent,TVE_EXPAND);
}
HTREEITEM CXtreeCtrl::CopyBranch(HTREEITEM htiBranch, HTREEITEM htiNewParent, HTREEITEM htiAfter) //拷贝分支
{
    ASSERT((htiNewParent != NULL) && (htiBranch != NULL));
    HTREEITEM hChild;
    hChild = GetChildItem( htiBranch );
    while ( hChild != NULL )
    {
        HTREEITEM hChildDest = InsertItem(_T("dest child"), htiNewParent);
        CopyBranch( hChild,hChildDest,htiAfter );
        CopyItem(hChild,hChildDest,htiAfter);
        hChild = GetNextSiblingItem( hChild );
    }
    return htiNewParent;
}

HTREEITEM CXtreeCtrl::InsertItemAndSubtree(HTREEITEM hParent)
{
    if ((m_hItemDragD == NULL) || (m_hItemDragS == NULL)) 
    {
        ASSERT(FALSE);
        return NULL;
    }
    HTREEITEM hRoot = GetRootItem();
    if (!ItemHasChildren(m_hItemDragD))     //如果注释可以随意拖拽父节点到子节点(自己看效果)
    {
        return  NULL;
    }
    
    else if (hRoot == m_hItemDragD)         //如果注释可以随意拖拽父节点至根节点(自己看效果)
    {
        return  NULL;
    }
    if (hParent == NULL)
        hParent = GetParentItem(m_hItemDragD);
    if (hParent == NULL)
        hParent = TVI_ROOT;

    HTREEITEM hInsertAfter;

    if (m_bInsertAbove)
        hInsertAfter = GetPrevSiblingItem(m_hItemDragD);
    else
        hInsertAfter = m_hItemDragD;

    if ((hInsertAfter == hParent) || (hInsertAfter == NULL))
        hInsertAfter = TVI_FIRST;

    HTREEITEM hNew = InsertItem(_T("dummy"), hParent, hInsertAfter);
    if (hNew == NULL)
        return NULL;

    CopyBranch(m_hItemDragS,hNew,m_hItemDragD);
    CopyItem(m_hItemDragS,hNew,m_hItemDragD);

    return hNew;
}

void CXtreeCtrl::OnLButtonUp(UINT nFlags, CPoint point)
{
    // TODO: Add your message handler code here and/or call default
    CTreeCtrl::OnLButtonUp(nFlags, point);
    if(m_bDragging)
    {
        m_bLeftBtnUp = TRUE;
        KillTimer(m_nScrollTimerID);
        m_nScrollTimerID = 0;
        KillTimer(m_nHoverTimerID);
        m_nHoverTimerID = 0;
        ReleaseCapture();
        ShowCursor(true);
        m_bDragging = false;
        SelectDropTarget(NULL);
        if (m_bLeftBtnUp)
        {
            if (m_hItemDragD != NULL)
                m_bGoingOK = TRUE;
            HTREEITEM m_hParentNew = m_hItemDragD;
            while (m_hParentNew != NULL)
            {

                if(m_hParentNew == m_hItemDragS)
                {
                    //AfxMessageBox(_T("禁止此操作!"), MB_ICONEXCLAMATION);
                    m_bGoingOK = FALSE;
                    break;
                }
                m_hParentNew = GetParentItem(m_hParentNew);
            }
            HTREEITEM m_hParent;
            if (m_bGoingOK)
            {
                SetRedraw(FALSE);            
                m_hParent = GetParentItem(m_hItemDragD);
                if (m_hParent == NULL)
                    m_hParent = TVI_ROOT;       
                if (m_bGoingOK)
                {
                    // We're finally ready to insert the actual item.
                    HTREEITEM hNew = InsertItemAndSubtree(m_hParent);
                    if (hNew != NULL) 
                    {
                        SelectItem(hNew);
                        DeleteItem(m_hItemDragS);
                    }
                }
            }
            // Regardless of what happens, we need to reset the tree.
            SetRedraw(TRUE);            
            Invalidate();
        }
        else
        {
            // The User chose to abort the drag
            EnsureVisible(m_hItemDragS);
            // Refresh the screen to get rid of any remnants of the drag drawing
            Invalidate();
        }

    }
    CTreeCtrl::OnLButtonUp(nFlags, point);
}

void CXtreeCtrl::OnTimer(UINT_PTR 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);
}

void CXtreeCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
    // TODO: Add your message handler code here and/or call default
    //m_dwDragStart = GetTickCount();   
    //m_hSelectItem = GetSelectedItem();
    //UINT nHitFlags = 0;
    //HTREEITEM hClickedItem = HitTest( point, &nHitFlags );
    CTreeCtrl::OnLButtonDown(nFlags, point);
}

XTreeCtrl.h

#pragma once


// CXtreeCtrl

class CXtreeCtrl : public CTreeCtrl
{
    DECLARE_DYNAMIC(CXtreeCtrl)

public:
    CXtreeCtrl();
    virtual ~CXtreeCtrl();

protected:
    DECLARE_MESSAGE_MAP()


public:
    afx_msg void OnTvnBegindrag(NMHDR *pNMHDR, LRESULT *pResult);
    afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
    afx_msg void OnMouseMove(UINT nFlags, CPoint point);
    afx_msg void OnTimer(UINT_PTR nIDEvent);
    afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
protected:
    DWORD       m_dwDragStart;
    UINT        m_nScrollTimerID;
    UINT        m_nHoverTimerID;
    UINT        m_TimerTicks;
    POINT       m_HoverPoint;
    BOOL        m_bDragging;
    BOOL        m_bInsertAbove;
    BOOL        m_bLeftBtnUp;
    BOOL        m_bGoingOK;
    CImageList* m_pDragImage;
    HTREEITEM   m_hItemDragS;
    HTREEITEM   m_hItemDragD;
    HTREEITEM   m_hSelectItem;
    HTREEITEM CopyBranch(HTREEITEM htiBranch,HTREEITEM htiNewParent,HTREEITEM htiAfter);
    HTREEITEM CopyItem(HTREEITEM hItem,HTREEITEM htiNewParent,HTREEITEM htiAfter);
    HTREEITEM GetDragTarget(HTREEITEM hItem, POINT point, BOOL& bInsertAbove);
    HTREEITEM InsertItemAndSubtree(HTREEITEM hParent = NULL);
};

猜你喜欢

转载自www.cnblogs.com/gaozihan/p/10835764.html
MFC