MFC:树形控件

成员函数

函数名称 功能
CTreeCtrlDlg(CWnd* pParent = NULL); 构造函数
void InitTreeFile(); 初始化树形控件,如果复制或用其他对象初始化时需要调用
BOOL SetTreeRoot(const CString strPath); 设置根目录
void SetUnwantedString(const std::vector vecStr); 设置不想显示的子目录
BOOL SetAcceptMsgWnd(const HWND hWnd); 设置接收消息的窗口句柄
CString GetPath(); 获取当前被选中的目录
BOOL SetTreeImageList(CImageList * ImgList, int nImgList); 设置当前控件目录图标
InitTreeFile()

该函数是在通过一个对象来初始化另一个对象或把一个对象赋值给另一个对象后需要调用该函数,以初始化控件。

例如:

CSelfUpdateTreeCtrl * pTreeFile;
pTreeFile = new CSelfUpdateTreeCtrl(m_TreeFile); // m_TreeFile是另一个控件
pTreeFile->Create(TVS_EDITLABELS | TVS_HASBUTTONS | WS_CHILD | WS_VISIBLE,
        rect,
        this,
        IDC_TREE_NODE);
pTreeFile->ShowWindow(SW_SHOWNORMAL);
pTreeFile->InitTreeFile(); // 这里调用初始化函数,初始化控件
SetTreeRoot(const CString strPath)

设置控件根目录,在想要改变控件的根目录或者在创建对象的时候没有提供根目录的时候调用。返回值为BOOL型,如果返回值是TRUE,则成功。如果返回值是FALSE,则失败。

例如:

CSelfUpdateTreeCtrl treeCtrl;
treeCtrl.SetTreeRoot(_T("E:\"));
SetUnwantedString(const std::vector vecStr)

不想有些目录出现,则可通过该函数设置一些要屏蔽的目录。

例如:

std::vector<CString> vecTemp;
vecTemp.push_back(_T("二值图"));
vecTemp.push_back(_T("缺陷大图"));
vecTemp.push_back(_T("实时图"));
vecTemp.push_back(_T("差影图"));
m_TreeFile.SetTreeRoot(m_strGlobalPath);
m_TreeFile.SetUnwantedString(vecTemp); // 设置不想显示文件夹的字符串

SetAcceptMsgWnd(const HWND hWnd)

如果需要其他窗口响应控件的焦点变化的消息则需要调用该函数来设置接收消息的窗口。而响应的窗口需要响应相关的消息。其消息为:WM_SELFUPDATETREECTRL_SELCHANGED。返回值为BOOL型,如果返回值是TRUE,则成功。如果返回值是FALSE,则失败。

例如:

std::vector<CString> vecTemp;
vecTemp.push_back(_T("二值图"));
vecTemp.push_back(_T("缺陷大图"));
vecTemp.push_back(_T("实时图"));
vecTemp.push_back(_T("差影图"));
m_TreeFile.SetTreeRoot(m_strGlobalPath);
m_TreeFile.SetUnwantedString(vecTemp);
m_TreeFile.SetAcceptMsgWnd(m_hWnd); // 设置接收消息的窗口

GetPath()

该函数是获取当前选中的节点的绝对路径。返回一个CString的,就是路径地址。

例如:

afx_msg LRESULT CBrowseDlg::OnSelfUpdateTreeMsg(WPARAM wParam,LPARAM lParam)
{
    LoadInformation(m_TreeFile.GetPath());
    return 0;
}

SetTreeImageList(CImageList * ImgList, int nImgList)

设置控件中节点的图标。返回值为BOOL型,如果返回值是TRUE,则成功。如果返回值是FALSE,则失败。


HICON hIcon = theApp.LoadIcon(IDI_ICON_TREEFILE);
CImageList imgList;
imgList.Create(16, 16, ILC_COLOR32, 3, 3);
for (int i = 0; i < 8; i++)
{
    imgList.Add(hIcon);
}
m_TreeFile.SetTreeImageList(&imgList, LVSIL_NORMAL);

使用案例

静态案例

声明为类成员变量

// 在头文件中声明为类
CSelfUpdateTreeCtrl m_TreeFile;

// 初始化控件
std::vector<CString> vecTemp;
vecTemp.push_back(_T("二值图"));
vecTemp.push_back(_T("缺陷大图"));
vecTemp.push_back(_T("实时图"));
vecTemp.push_back(_T("差影图"));
m_TreeFile.SetTreeRoot(m_strGlobalPath);
m_TreeFile.SetUnwantedString(vecTemp);
m_TreeFile.SetAcceptMsgWnd(m_hWnd);

HICON hIcon = theApp.LoadIcon(IDI_ICON_TREEFILE);
CImageList imgList;
imgList.Create(16, 16, ILC_COLOR32, 3, 3);
for (int i = 0; i < 8; i++)
{
    imgList.Add(hIcon);
}
m_TreeFile.SetTreeImageList(&imgList, LVSIL_NORMAL);

动态案例
CRect rect(300,0, 500,300);
m_pTreeFile = new CSelfUpdateTreeCtrl(m_TreeFile); // 动态创建树形控件

m_pTreeFile->Create(TVS_EDITLABELS | TVS_HASBUTTONS | WS_CHILD | WS_VISIBLE,
    rect,
    this,
    IDC_TREE_NODE);
m_pTreeFile->ShowWindow(SW_SHOWNORMAL);
m_pTreeFile->InitTreeFile();

源代码
// 头文件
#pragma once
#include <vector>
// Tree结构

#define WM_SELFUPDATETREECTRL_SELCHANGED WM_USER + 779
// CSelfUpdateTreeCtrl

class CSelfUpdateTreeCtrl : public CTreeCtrl
{
	DECLARE_DYNAMIC(CSelfUpdateTreeCtrl)

public:
	CSelfUpdateTreeCtrl();
	CSelfUpdateTreeCtrl(CString strPath);
	virtual ~CSelfUpdateTreeCtrl();
    CSelfUpdateTreeCtrl(CSelfUpdateTreeCtrl & ob);
    CSelfUpdateTreeCtrl & operator= (CSelfUpdateTreeCtrl & ob);
protected:
	DECLARE_MESSAGE_MAP()

private:
	CString m_strRoot;                            // 根目录
    HWND m_hAcceptMessage;                        // 接收消息的窗口
	std::vector<HTREEITEM> m_vecTreeTop;          // 目录节点树,每一个元素代表一个*目录
    std::vector<HTREEITEM> m_vecHierarchy;        // 目录节点用于遍历*目录下的子目录
    std::vector<CString> m_vecUnwantedString;     // 不想出现在树形结构上的目录名称列表
    CImageList m_ImgList;                      // 目录树图标列表
    int m_nImgList;
    // 初始化根目录
	BOOL InitRootDirectory();
    // 更新树节点
    BOOL UpdateTreeNode(const unsigned long nIndex);
    // 判断当前节点级别
    int JudgeFloor();
    // 插入树节点
	BOOL InsertTreeNode(const CString strRoot, const unsigned long nIndex);
    // 递归插入
    BOOL InsertRecursion(const CString strRoot, int nFileNum, const unsigned long nIndex);
    // 拷贝图标列表数据
    BOOL SetTreeImageList(CImageList * ImgList);
public:
    // 初始化树形控件,如果复制或用其他对象初始化时需要调用
    void InitTreeFile();
    // 设置根目录
	BOOL SetTreeRoot(const CString strPath);
    // 设置不想显示的目录
    void SetUnwantedString(const std::vector<CString> vecStr);
    // 设置接收消息的窗口
    BOOL SetAcceptMsgWnd(const HWND hWnd);
    // 获取当前目录
    CString GetPath();
    // 发送消息函数
    afx_msg void OnTvnSelchanged(NMHDR * pNMHDR, LRESULT * pResult);
    // 设置图标列表
    BOOL SetTreeImageList(CImageList * ImgList, int nImgList);
};
// 源文件
// SelfUpdateTreeCtrl.cpp : 实现文件
//

#include "stdafx.h"
#include "SelfUpdateTreeCtrl.h"
#include <algorithm>

// CSelfUpdateTreeCtrl

IMPLEMENT_DYNAMIC(CSelfUpdateTreeCtrl, CTreeCtrl)

CSelfUpdateTreeCtrl::CSelfUpdateTreeCtrl()
{
	m_strRoot = "";
    m_hAcceptMessage = NULL;
    m_nImgList = LVSIL_NORMAL;
}

CSelfUpdateTreeCtrl::CSelfUpdateTreeCtrl(CString strPath) : m_strRoot(strPath)
{
    m_hAcceptMessage = NULL;
    m_nImgList = LVSIL_NORMAL;
	InitRootDirectory();
}

CSelfUpdateTreeCtrl::~CSelfUpdateTreeCtrl()
{
   if ( NULL != m_ImgList.GetSafeHandle())
   {
       m_ImgList.DeleteImageList();
       ASSERT(m_ImgList.GetSafeHandle() == NULL);
   }
}

CSelfUpdateTreeCtrl::CSelfUpdateTreeCtrl(CSelfUpdateTreeCtrl & ob)
{
    m_strRoot = ob.m_strRoot;
    m_hAcceptMessage = ob.m_hAcceptMessage;
    if (!m_vecTreeTop.empty())
    {
        m_vecTreeTop.clear();
    }
    if (!m_vecHierarchy.empty())
    {
        m_vecHierarchy.clear();
    }
    if (!m_vecUnwantedString.empty())
    {
        m_vecUnwantedString.clear();
    }
    m_vecTreeTop = ob.m_vecTreeTop;
    m_vecHierarchy = ob.m_vecHierarchy;
    m_vecUnwantedString = ob.m_vecUnwantedString;
    //图标列表
    m_nImgList = ob.m_nImgList;
    SetTreeImageList( &(ob.m_ImgList));
}



CSelfUpdateTreeCtrl & CSelfUpdateTreeCtrl::operator= (CSelfUpdateTreeCtrl & ob)
{
    //CTreeCtrl::operator= (ob);
    if (this == &ob)
        return * this;
    m_strRoot = ob.m_strRoot;
    m_hAcceptMessage = ob.m_hAcceptMessage;
    if (!m_vecTreeTop.empty())
    {
        m_vecTreeTop.clear();
    }
    if (!m_vecHierarchy.empty())
    {
        m_vecHierarchy.clear();
    }
    if (!m_vecUnwantedString.empty())
    {
        m_vecUnwantedString.clear();
    }
    m_vecTreeTop = ob.m_vecTreeTop;
    m_vecHierarchy = ob.m_vecHierarchy;
    m_vecUnwantedString = ob.m_vecUnwantedString;
    // 图标列表
    m_nImgList = ob.m_nImgList;
    if (m_ImgList.GetSafeHandle())
    {
        m_ImgList.DeleteImageList();
        ASSERT(m_ImgList.GetSafeHandle() == NULL);
    }
    SetTreeImageList(&(ob.m_ImgList));

    return *this;
}

BEGIN_MESSAGE_MAP(CSelfUpdateTreeCtrl, CTreeCtrl)
    ON_NOTIFY_REFLECT(TVN_SELCHANGED, &CSelfUpdateTreeCtrl::OnTvnSelchanged)
END_MESSAGE_MAP()



// CSelfUpdateTreeCtrl 消息处理程序
BOOL CSelfUpdateTreeCtrl::SetTreeRoot(CString strPath)
{
	m_strRoot = strPath;
	return InitRootDirectory();
}

BOOL CSelfUpdateTreeCtrl::InitRootDirectory()
{
    if ("" == m_strRoot)
    {
        return FALSE;
    }
    if (!m_vecTreeTop.empty())
    {
        m_vecTreeTop.clear();
    }
    CFileFind file;
    CString strDirectory = m_strRoot;
    if ( strDirectory.Right(1) != "\" )
    {
        strDirectory += _T("\");
    }
    strDirectory += _T("*.*");
    BOOL bRet = file.FindFile(strDirectory);
    unsigned long ulNum = 0;// 给每个结点设置索引号
    while(bRet)
    {
        bRet = file.FindNextFile();  // 是否有下一个目录
        if (file.IsDirectory() && !file.IsDots())
        {
            CString strPath = file.GetFilePath();
            CString strTitle = strPath.Right(strPath.GetLength()-strPath.ReverseFind('\')-1);
            HTREEITEM hItem = InsertItem(strTitle, 0, 0, NULL);
            m_vecTreeTop.push_back(hItem);
            SetItemData(hItem, ulNum);
            ulNum++;
        }
    }
    file.Close();
    return TRUE;
}

void CSelfUpdateTreeCtrl::OnTvnSelchanged(NMHDR *pNMHDR, LRESULT *pResult)
{
    LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
    // TODO: 在此添加控件通知处理程序代码
    // 获取当前选中节点深度
    int nFloor = JudgeFloor();
    // 更新当前目录
    if ( 0 == nFloor && !ItemHasChildren(GetSelectedItem()))
    {
        unsigned long ulNum = GetItemData(GetSelectedItem());
        UpdateTreeNode(ulNum);
    }
    else
    {
        if (m_hAcceptMessage)
        {
            ::PostMessage(m_hAcceptMessage, WM_SELFUPDATETREECTRL_SELCHANGED, 0, 0);
        }
    }
    *pResult = 0;
}

int CSelfUpdateTreeCtrl::JudgeFloor()
 {
     int nDepth = 0;
     HTREEITEM hCurrentNode = GetSelectedItem();
     while ( (hCurrentNode = GetParentItem(hCurrentNode)) != NULL)
     {
         nDepth++;
     }
     return nDepth;
 }

BOOL CSelfUpdateTreeCtrl::UpdateTreeNode(unsigned long nIndex)
{
    // 调用插入节点函数
    return InsertTreeNode(GetPath(), nIndex);
}

BOOL CSelfUpdateTreeCtrl::InsertTreeNode(CString strRoot, unsigned long nIndex)
{

    if ( "" == strRoot || nIndex >= m_vecTreeTop.size())
    {
        return FALSE;
    }
    BOOL bRet = InsertRecursion(strRoot, 0, nIndex);
    m_vecHierarchy.clear();
    return bRet;
}

BOOL CSelfUpdateTreeCtrl::InsertRecursion(CString strRoot, int nFileNum, unsigned long nIndex)
{
    nFileNum++;//这个一定要放在switch之前,不能放在下面
    CFileFind file;
    CString strDirectory = strRoot;
    if ( strDirectory.Right(1) != "\" )
    {
        strDirectory += _T("\");
    }
    strDirectory += _T("*.*");
    BOOL bRet = file.FindFile(strDirectory);
    while(bRet)
    {
        bRet = file.FindNextFile();  // 是否有下一个目录
        if (file.IsDirectory() && !file.IsDots())
        {
            CString strPath = file.GetFilePath();
            CString strTitle = strPath.Right(strPath.GetLength()-strPath.ReverseFind('\')-1);
            if(std::find(m_vecUnwantedString.begin(), m_vecUnwantedString.end(), strTitle) != m_vecUnwantedString.end())
            {
                continue;
            }
            switch( nFileNum )
            {
            case 1:
                if (m_vecHierarchy.empty())
                {
                     m_vecHierarchy.push_back(InsertItem(strTitle, nFileNum, nFileNum, m_vecTreeTop[nIndex]));
                }
                else
                {
                    m_vecHierarchy[nFileNum - 1] = InsertItem(strTitle, nFileNum, nFileNum,m_vecTreeTop[nIndex]);
                }
                break;
            default:
                if ( m_vecHierarchy.size() <= nFileNum - 1 )
                {
                    m_vecHierarchy.push_back(InsertItem(strTitle, nFileNum, nFileNum,m_vecHierarchy[nFileNum - 2]));
                }
                else
                {
                    m_vecHierarchy[nFileNum - 1] = InsertItem(strTitle, nFileNum, nFileNum,m_vecHierarchy[nFileNum - 2]);
                }
                break;
            }
            InsertRecursion(strPath, nFileNum, nIndex);//递归遍历子目录
        }
        else if( !file.IsDirectory() && !file.IsDots() )//如果不是一个目录,并且也不是当前目录
        {
            ;//暂时不处理其他类型的图片
        }
    }//是否找到文件
    file.Close();
    return TRUE;
}

void CSelfUpdateTreeCtrl::SetUnwantedString(std::vector<CString> vecStr)
{
    m_vecUnwantedString = vecStr;
}

 BOOL CSelfUpdateTreeCtrl::SetAcceptMsgWnd(HWND hWnd)
 {
     if (hWnd)
     {
         m_hAcceptMessage = hWnd;
         return TRUE;
     }
     else
     {
         return FALSE;
     }
 }

CString CSelfUpdateTreeCtrl::GetPath()
{
    HTREEITEM CurrentNode = GetSelectedItem();
    HTREEITEM ParentNode = GetParentItem(CurrentNode);
    CString strPath = GetItemText(ParentNode);
    CString strSelf = GetItemText(CurrentNode);
    if ("" == strPath)
    {
        strPath += strSelf+_T("\");
    }
    else
    {
        strPath += _T("\") + strSelf+_T("\");
    }
    while((ParentNode = GetParentItem(ParentNode))!=NULL)
    {
        CString strTemp = GetItemText(ParentNode);
        strPath = strTemp + _T("\") + strPath;
    }
    strPath = m_strRoot + _T("\") + strPath;
    return strPath;
}

BOOL CSelfUpdateTreeCtrl::SetTreeImageList(CImageList * ImgList, int nImgList)
{
    if ((NULL == ImgList->GetSafeHandle()) || (ImgList == &m_ImgList))
    {
        return FALSE;
    }
    if ( m_ImgList.GetSafeHandle() )
    {
        m_ImgList.DeleteImageList();
        ASSERT( m_ImgList.GetSafeHandle() == NULL);
    }
    m_ImgList.Create(ImgList);
    m_nImgList = nImgList;
    SetImageList(&m_ImgList, m_nImgList);
    return TRUE;
}

BOOL CSelfUpdateTreeCtrl::SetTreeImageList(CImageList * ImgList)
{
    if ((NULL == ImgList->GetSafeHandle()) || (ImgList == &m_ImgList))
    {
        return FALSE;
    }
    if ( m_ImgList.GetSafeHandle() )
    {
        m_ImgList.DeleteImageList();
        ASSERT( m_ImgList.GetSafeHandle() == NULL);
    }
    m_ImgList.Create(ImgList);
    return TRUE;
}

 void CSelfUpdateTreeCtrl::InitTreeFile()
 {
     if (!m_vecTreeTop.empty())
     {
         m_vecTreeTop.clear();
         DeleteAllItems();
     }
     SetImageList(&m_ImgList, m_nImgList);
     InitRootDirectory();
 }

附言

该类继承自类CTreeCtrl,重载了“=”操作符和拷贝构造函数,可以直接把一个对象赋值给另一个对象,或者用一个对象初始化另一个对象。但是之后需要调用InitTreeFile()函数。

这里是采用了缓加载目录的方法,当初始化后会只加载*目录,子目录是没有加载的。这样可以避免当目录过多的时候导致加载时间过长的现象。子目录是当焦点转移到*目录的时候才加载相关目录下面的子目录。

如下图:

MFC:树形控件