65.9K
CodeProject 正在变化。 阅读更多。
Home

树控件提供拖放、文件夹自动展开、新建/编辑/删除和按钮移动

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.74/5 (23投票s)

2010 年 1 月 21 日

CPOL

3分钟阅读

viewsIcon

65890

downloadIcon

2582

一个基本的树控件,将所有功能结合在一起,使项目完全可编辑和可移动。

ExDragDropCtrl

引言

此代码将我真正希望我的完全可拖动/可移动的树控件能够做到的许多事情结合在一起,包括添加新文件夹、重命名任何项目和删除任何项目。此控件允许的特定功能包括

  • 完整的拖放功能,包括拖动图像和指示器,以显示项目将被放置的位置,例如,帮助区分将项目放置在文件夹中或文件夹之后
  • 上下移动按钮,用于简单的项目导航,包括进入和退出文件夹
  • 新建文件夹、重命名和删除,包括删除整个文件夹

背景

此代码最初基于 Frederic My 在 www.fairyengine.com 的免费树控件。 感谢 Frederic。

Using the Code

扩展的树控件 (CTreeCtrlDrag) 包含在源文件 TreeCtrlDrag.cpp.h 中。

要使用该控件,只需在您的对话框中将 CTreeCtrl 声明替换为 CTreeCtrlDrag。 由于它派生自 CTreeCtrl,因此可以像往常一样初始化。 然后,对话框需要将任何用户输入传递给树控件,如以下代码所示,该代码将在对话框类中...

//////////////////////////////////////////////////////////////////////
// Move tree item one place up
void CDragDropView::OnBtnUp()
{
    m_Tree.MoveItemUp();
    m_Tree.SetFocus();
}

//////////////////////////////////////////////////////////////////////
// Move tree item one place down
void CDragDropView::OnBtnDown()
{
    m_Tree.MoveItemDown();
    m_Tree.SetFocus();
}

//////////////////////////////////////////////////////////////////////
// Tree selection has changed, so check status of Up and Down buttons
void CDragDropView::OnTreeSelChanged(NMHDR* pNMHDR, LRESULT* pResult)
{
    m_fbtnUp.EnableWindow(m_Tree.ShouldUpBtnBeEnabled());
    m_fbtnDown.EnableWindow(m_Tree.ShouldDownBtnBeEnabled());
}

void CDragDropView::OnBtnNewFolder()
{
 m_Tree.CreateNewFolder();
}

void CDragDropView::OnBtnRename()
{
 m_Tree.RenameSelected();
}

void CDragDropView::OnBtnDelete()
{
 m_Tree.DeleteSelected();
}

在我的实际使用中,我将每个树项链接到一个数据结构,因此我需要知道何时删除或移动项目。 当移动树项时,实际上会在目标位置将其删除并重新创建。 因此,我需要知道旧的树项和新的树项是什么。 这将在 CTreeCtrlDrag 控件中自动完成,并以用户定义的、发布回父窗口(对话框)的消息的形式出现。 它们将以这种方式在调用的对话框中处理...

BEGIN_MESSAGE_MAP(CDlgQuickGraphOrganise, CDialog)
 //{{AFX_MSG_MAP(CDlgQuickGraphOrganise)
    ON_MESSAGE(UWM_TREEMOVECOMPLETE, OnTreeMoveComplete)
    ON_MESSAGE(UWM_TREEITEMDELETED, OnItemDelete)
    ON_MESSAGE(UWM_TREEBEGINEDIT, OnBeginLabelEdit)
    ON_MESSAGE(UWM_TREEENDEDIT, OnItemRenamed)
    ON_MESSAGE(UWM_TREERBUTTONDOWN, OnRButtonDown)
    ON_MESSAGE(UWM_TREELBUTTONDBLCLICK, OnBtnCreategraph)
 //}}AFX_MSG_MAP
END_MESSAGE_MAP()

..

//============================================================
// Tree Callback Message Handlers
//============================================================
//////////////////////////////////////////////////////////////
// A tree item has been moved successfully,
// so swap references to the old tree item with the new
// one. In other words, a tree item has been deleted
// and recreated somewhere else.
void CDlgQuickGraphOrganise::OnTreeMoveComplete(WPARAM wParam,
                                                LPARAM lParam)
{
    HTREEITEM hOld = (HTREEITEM)wParam;
    HTREEITEM hNew = (HTREEITEM)lParam;
    // Sanity check the move parameters
    ASSERT((hNew != NULL) && (hOld != NULL));
    ...
}

//////////////////////////////////////////////////////////////
// A tree item has been deleted successfully,
// so add it to the Deleted Items list
void CDlgQuickGraphOrganise::OnItemDelete(WPARAM wParam,
                                          LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    HTREEITEM hOld = (HTREEITEM)wParam;
    ...
}

//////////////////////////////////////////////////////////////
// If the user is trying to edit the root item,
// make a beep sound and cancel the edit by setting
// the pResult value to 1.
void CDlgQuickGraphSummary::OnBeginLabelEdit(NMHDR *pNMHDR,
                                             LRESULT *pResult)
{
    LPNMTVDISPINFO pTVDispInfo =
       reinterpret_cast<LPNMTVDISPINFO>(pNMHDR);
    HTREEITEM hItem = pTVDispInfo->item.hItem;
    if (hItem == m_Tree.GetRootItem())
    {
        // We don't allow the user to edit the root folder.
        *pResult = 1;
        MessageBeep((UINT)-1);
    }
}

//////////////////////////////////////////////////////////////
// We've successfully changed an item name - we may want to
// post-process the changed item.
void CDlgQuickGraphOrganise::OnItemRenamed(WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    HTREEITEM hItem = (HTREEITEM)wParam;
    ...
}

在这种情况下,用户定义的消息(例如,UWM_TREEBEGINEDIT)在 StdAfx.h 中设置。

其他需要注意的几点

  • CTreeCtrlDrag 需要能够确定文件夹中的特定树项是否可以为空,因此它假定用于文件夹的图像始终相同,即,使用 FOLDER_IMAGE 定义。 如果您使用图像列表并且文件夹图像的索引因使用而异,则您必须在这里做一些更聪明的事情。

关注点

虽然我一直在编码大约 4 年,但我知道我仍然有很多东西要学。 请,如果您看到任何改进此控件或我一般风格的方法,所有建设性的反馈将不胜感激。 注意 - 这仅涉及 TreeCtrlDrag.cpp.h,这些是我特别处理的文件。

历史

  • 2010 年 1 月 21 日 - 初始发布
  • 2010 年 1 月 26 日 - 修复了 TidyUpEndOfDrag() 中的内存泄漏 - 感谢 Tom
  • 2010 年 1 月 26 日 - 在移动/重命名时添加了对重复项目的检查。 设置树时,调用 m_Tree.SetNoDuplicates(TRUE),以打开对当前树文件夹中重复项目的检查。 请参阅移动发生后和重命名后(在 OnEndLabelEdit() 中)的附加代码。
  • 2010 年 2 月 17 日 - 修复了使用上下按钮移动文件夹的返回消息中的一个错误,其中该消息仅针对父项返回,而不是任何子项。 添加了一个静态显示父对话框收到的消息。 添加了双击响应到调用对话框,以处理对非树项的双击。 增加了右键单击的回调,以允许调用对话框访问上下文菜单选项。
  • 2010 年 3 月 29 日 - 修复了控件中的一个错误
© . All rights reserved.