拖放和背景图像树控件






2.42/5 (43投票s)
2003年1月15日
4分钟阅读

181282

8050
一篇文章,允许轻松拖放和位图背景以及其他树控件功能
引言
这是我的第一篇 CodeProject 文章,所以如果有人给我低分,我真的很想知道原因(以便改进)。由于我一直是 CodeProject.com 的忠实读者大约 3-4 年,我决定最终提交我手中许多可能对他人有用的东西之一。
本文详细介绍了如何使用我的 cTree
类,该类允许轻松实现拖放功能,并快速为 TreeCtrl
创建背景图像。我的图片和上面示例图像中的阴影 URL 都是 TreeCtrl
背景的一部分!我无法捕捉真实的“禁止”图标,因为屏幕截图无法捕捉鼠标图标,所以我不得不在 Photoshop 中制作(因此,“禁止”图标异常粗大 :-))。但上面的图像只是为了让你了解我的类能做什么,所以下载它来看看吧!
该类还允许你无需任何操作即可实现拖放,并且它只允许在根节点之间进行拖放(如果你愿意,可以轻松更改此设置)。
代码详情
所有核心内容都位于 cTree.cpp 和 cTree.h 中。
拖放是通过 OnBeginDrag()
和 OnLButtonUp()
实现的。在 OnBeginDrag
中,我使用 NMHDR
强制转换为 NM_TREEVIEW
结构
。
void cTree::OnBeginDrag(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
*pResult = 0;
// So user cant drag root node
if (GetParentItem(pNMTreeView->itemNew.hItem) == NULL) return ;
// Item user started dragging ...
m_hitemDrag = pNMTreeView->itemNew.hItem;
m_hitemDrop = NULL;
// get the image list for dragging
m_pDragImage = CreateDragImage(m_hitemDrag);
// CreateDragImage() returns NULL if no image list
// associated with the tree view control
if( !m_pDragImage )
return;
m_bLDragging = TRUE;
m_pDragImage->BeginDrag(0, CPoint(-15,-15));
POINT pt = pNMTreeView->ptDrag;
ClientToScreen( &pt );
m_pDragImage->DragEnter(NULL, pt);
SetCapture();
}
在将 NMHDR
强制转换为 NM_TREEVIEW
结构后,我首先要做的是测试实际的项是否是根节点。如果是,我直接返回,但如果你希望根节点可以拖动,可以轻松注释掉这一行。
然后,我将拖动的项保存到我的成员变量 m_itemDrag
中,它是一个 HTREEITEM
。m_itemDrop
为 NULL
,因为用户刚刚开始拖动该项。CreateDragImage()
返回一个 CImageList
类型的指针(它是与 HTREEITEM
关联的图像的副本),我们将其存储在 m_pDragImage
中。
接下来,我们需要在拖动开始的点绘制图像,并将布尔值 m_bLDragging
设置为 true
,这样我们就知道拖动已经开始。接下来的代码处理所有后续的鼠标移动,直到松开左键。
void cTree::OnMouseMove(UINT nFlags, CPoint point)
{
HTREEITEM hitem;
UINT flags;
if (m_bLDragging)
{
POINT pt = point;
ClientToScreen(&pt);
CImageList::DragMove(pt);
if ((hitem = HitTest(point, &flags)) != NULL)
{
CImageList::DragShowNolock(FALSE);
// Tests if dragged item is over another child !
if ((GetParentItem(hitem) != NULL) && (cursor_no != ::GetCursor()))
{
::SetCursor(cursor_no);
// Dont want last selected target highlighted after mouse
// has moved off of it, do we now ?
SelectDropTarget(NULL);
}
// Is item we're over a root node and not parent root node ?
if ((GetParentItem(hitem) == NULL) && (GetParentItem(m_hitemDrag) != hitem ))
{
if (cursor_arr != ::GetCursor()) ::SetCursor(cursor_arr);
SelectDropTarget(hitem);
}
m_hitemDrop = hitem;
CImageList::DragShowNolock(TRUE);
}
}
else
{
// Set cursor to arrow if not dragged
// Otherwise, cursor will stay hand or arrow depen. on prev setting
::SetCursor(cursor_arr);
}
CTreeCtrl::OnMouseMove(nFlags, point);
}
在 OnMouseMove()
的开头,我们检查布尔值 m_bLDragging
是否已设置为 true
,以便我们知道用户已开始拖动某个项。如果确实如此,那么我们获取当前的鼠标位置并将拖动的项移动到该位置。
接下来,我们调用 HitTest()
来查看当前鼠标位置是否位于某个项上方。如果不是,则返回 NULL
;否则,它会返回鼠标所在的项的 HTREEITEM
。我们调用 DragShowNoLock(FALSE)
来阻止 CImageList
在我们检查当前项状态时进行绘制(实际上是隐藏)。
接下来,我们检查从 HitTest()
返回的项是否不是子项,如果是,我们将光标设置为 cursor_no
,以便用户看到光标已更改为不允许的操作。如果该项位于根节点上方,我们将光标设置为箭头,并允许 CImageList
绘制被拖动的项。
在 CMainFrame
中,你可以看到创建 Treectrl
以及添加和删除组和子项是多么容易。
void CMainFrame::CreateTreeCtrl()
{
if (tree_ctrl) tree_ctrl.DestroyWindow() ;
CRect rect;
GetClientRect(&rect);
tree_ctrl.Create( /*|| TVS_EDITLABELS |*/ WS_VISIBLE |
WS_TABSTOP | WS_CHILD /*| WS_BORDER | LBS_NOTIFY*/
/*| TVS_LINESATROOT | TVS_HASLINES */
/*& TVS_NOTOOLTIPS & TVS_HASBUTTONS*/,
CRect(10,10,rect.right-5,rect.bottom),this,ID_TREELISTBOX);
CFont listBoxFont ;
listBoxFont.CreateFont(
16, // nHeight
0, // nWidth
0, // nEscapement
0, // nOrientation
FW_BOLD, // nWeight
FALSE, // bItalic
FALSE, // bUnderline
0, // cStrikeOut
ANSI_CHARSET, // nCharSet
OUT_DEFAULT_PRECIS, // nOutPrecision
CLIP_DEFAULT_PRECIS, // nClipPrecision
DEFAULT_QUALITY, // nQuality
DEFAULT_PITCH | FF_SWISS, // nPitchAndFamily
"Arial");
tree_ctrl.SetFont(&listBoxFont,FALSE);
COLORREF acolor=RGB(0,0,0);
tree_ctrl.SetTextColor(acolor);
CBitmap bmp;
// normal tree images
tree_imageList.Create(IDB_TREE_IMAGELIST,18,7, RGB(255, 0 ,255));
ASSERT(tree_imageList.m_hImageList);
bmp.LoadBitmap(IDB_TREE_IMAGELIST);
tree_imageList.Add( &bmp, RGB(255,255,255));
bmp.DeleteObject();
//TVSIL_STATE
tree_ctrl.SetImageList(&tree_imageList,TVSIL_NORMAL);
tree_ctrl.AddGroup("CodeProject") ;
tree_ctrl.AddGroup("DanCclark.com") ;
tree_ctrl.AddGroup("Peeps") ;
tree_ctrl.AddGroup("Folders") ;
tree_ctrl.AddChild("danclark","Other Contacts") ;
tree_ctrl.AddChild("fugazi","Other Contacts") ;
}
首先,我在 MainFrm.h 中声明了一个 cTree 类型的成员变量 tree_ctrl
,然后在 CreateTreeCtrl
中,我们调用 Create
并将我们希望 Treectrl
使用的字体类型传递给 SetFont
。然后我们创建一个 bitmap
,并将与 imagelist
关联的资源加载到其中,然后将其添加到 tree_imageList
中,最后将其传递给 TreeCtrl
对象 SetImageList()
。
此外,我在 cTree
的构造函数中放置了 SetDefaultCursor
函数,以便在创建时自动加载“禁止”光标和箭头。
我还决定不介绍绘制 TreeCtrl
背景的函数,因为整篇文章对大多数人来说太长了。
其他有用函数
AddGroup(CString groupname)
- 向TreeCtrl
添加组。DeleteGroup(CString groupname)
- 删除组。AddChild(CString childname,CString groupname)
- 向给定组添加一个子项。DeleteChild(CString childname,CString groupname)
- 删除给定组中的一个子项。GetChildCountInGroup(CString groupname)
- 返回组中的子项数量。SetBkImage(UINT)
- 使用资源 ID 设置TreeCtrl
的背景图像。SetBkImage(LPCTSTR)
- 使用字符串位置设置TreeCtrl
的背景图像。
许可证
本文未附加明确的许可证,但可能在文章文本或下载文件本身中包含使用条款。如有疑问,请通过下面的讨论区联系作者。
作者可能使用的许可证列表可以在此处找到。