Introduction
This is my very first CodeProject article, so if anyone gives me a low mark, I would truly like to know why (so that I may improve). Since I've been a faithful CodeProject.com reader for about 3-4 years, I decided to finally submit one of the many things I've had around that hopefully will be useful to someone else.
This article details how one can use my cTree
class that allows one to implement drag and drop easily and to create a background image for their TreeCtrl
rapidly. My picture and the shadowed URL in the sample image above are all part of the TreeCtrl
's background! I couldn't capture the real No-arrow, as capturing the screen does not capture the mouse icon so I had to make it in Photoshop (hence the abnormally fat No-Arrow :-)). But the above image is just to give you an idea of what my class can do, so download it to see !
This class allows you to also implement Drag & Drop without doing anything, and it only allows dragging to occur between root nodes (you can easily change this if you so desire).
Code Details
The core of everything is located in cTree.cpp and cTree.h.
Drag & Drop is implemented with OnBeginDrag()
and OnLButtonUp()
. In OnBeginDrag
I cast a NMHDR
to a NM_TREEVIEW
struct
:
void cTree::OnBeginDrag(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
*pResult = 0;
if (GetParentItem(pNMTreeView->itemNew.hItem) == NULL) return ;
m_hitemDrag = pNMTreeView->itemNew.hItem;
m_hitemDrop = NULL;
m_pDragImage = CreateDragImage(m_hitemDrag);
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();
}
The first thing I do after casting to a NM_TREEVIEW
struct is test whether or not the actual item is a root node. If so I return, though you can easily comment out this line if you want your root nodes to be draggable.
I then save the item dragged into my member var m_itemDrag
which is a HTREEITEM
. m_itemDrop
is NULL
as the user has just begun to drag the item. CreateDragImage()
returns a pointer of type CImageList
(which is a copy of the image associated with HTREEITEM
)that we store in m_pDragImage
.
Next, we need to draw the image at the point where the drag began and set the bool m_bLDragging
to true
so that we know that dragging has begun. The following code handles any subsequent mouse moves until the left mouse button is let go:
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);
if ((GetParentItem(hitem) != NULL) && (cursor_no != ::GetCursor()))
{
::SetCursor(cursor_no);
SelectDropTarget(NULL);
}
if ((GetParentItem(hitem) == NULL) && (GetParentItem(m_hitemDrag) != hitem ))
{
if (cursor_arr != ::GetCursor()) ::SetCursor(cursor_arr);
SelectDropTarget(hitem);
}
m_hitemDrop = hitem;
CImageList::DragShowNolock(TRUE);
}
}
else
{
::SetCursor(cursor_arr);
}
CTreeCtrl::OnMouseMove(nFlags, point);
}
In the beginning of OnMouseMove()
, we check the bool
m_bLDragging
to see if it has previously been set so we know that the user has started to drag an item. If so, then we get the current mouse position and move the dragged item to that position.
Next, we call HitTest()
to see if the current mouse position is over an item. If it is not, then NULL
is passed back, otherwise it passes us the HTREEITEM
of the item the mouse is over. We call DragShowNoLock(FALSE)
to prevent the CImageList
from drawing (it actually hides) while we are checking the status of the current item.
Next, we check that the item that was returned from HitTest()
is not a child item, and if it is, we set the cursor to that of the cursor_no
so the user sees the cursor change to that of an action that is not permitted. If the item is over a root node, we set the cursor to an arrow and proceed allowing the CImageList
to draw the dragged item.
Within CMainFrame
, you can see just how easy it is to create a Treectrl
and add and delete groups and children:
void CMainFrame::CreateTreeCtrl()
{
if (tree_ctrl) tree_ctrl.DestroyWindow() ;
CRect rect;
GetClientRect(&rect);
tree_ctrl.Create( WS_VISIBLE |
WS_TABSTOP | WS_CHILD
,
CRect(10,10,rect.right-5,rect.bottom),this,ID_TREELISTBOX);
CFont listBoxFont ;
listBoxFont.CreateFont(
16,
0,
0,
0,
FW_BOLD,
FALSE,
FALSE,
0,
ANSI_CHARSET,
OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY,
DEFAULT_PITCH | FF_SWISS,
"Arial");
tree_ctrl.SetFont(&listBoxFont,FALSE);
COLORREF acolor=RGB(0,0,0);
tree_ctrl.SetTextColor(acolor);
CBitmap bmp;
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();
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") ;
}
First, I declare a member of cTree called tree_ctrl
within MainFrm.h, and within CreateTreeCtrl
, we call Create
and pass SetFont
the font type we want our Treectrl
to use. We then create a bitmap
that we load the resource associated with imagelist
into and Add it to the tree_imageList
that is then passed to TreeCtrl
object SetImageList()
.
Also, I put the function SetDefaultCursor
within the cTree
's constructor so that it automatically loads the No Cursor and the arrow on creation.
I also decided not to cover the functions that do the drawing of the background of the TreeCtrl
as this whole article would be too long for most to read.
Other Useful Functions
AddGroup(CString groupname)
- Adds a group to the TreeCtrl
DeleteGroup(CString groupname)
- Deletes a group AddChild(CString childname,CString groupname)
- Adds a buddy to the given group DeleteChild(CString childname,CString groupname)
- Deletes a buddy in the given group GetChildCountInGroup(CString groupname)
- Returns number of children in a group SetBkImage(UINT)
- Sets the background image of the TreeCtrl
given the resource ID SetBkImage(LPCTSTR)
- Sets the background image of the TreeCtrl
given the string
location
License
This article has no explicit license attached to it, but may contain usage terms in the article text or the download files themselves. If in doubt, please contact the author via the discussion board below.
A list of licenses authors might use can be found here.