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






4.74/5 (23投票s)
一个基本的树控件,将所有功能结合在一起,使项目完全可编辑和可移动。

引言
此代码将我真正希望我的完全可拖动/可移动的树控件能够做到的许多事情结合在一起,包括添加新文件夹、重命名任何项目和删除任何项目。此控件允许的特定功能包括
- 完整的拖放功能,包括拖动图像和指示器,以显示项目将被放置的位置,例如,帮助区分将项目放置在文件夹中或文件夹之后
- 上下移动按钮,用于简单的项目导航,包括进入和退出文件夹
- 新建文件夹、重命名和删除,包括删除整个文件夹
背景
此代码最初基于 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 日 - 修复了控件中的一个错误