Click here to Skip to main content
15,867,308 members
Articles / Desktop Programming / MFC

Drag & Drop & Background Image Tree Control

Rate me:
Please Sign up or sign in to vote.
2.42/5 (43 votes)
14 Jan 20034 min read 176.7K   8K   43   29
An article that allows easy drag & drop and a bitmap background with other tree control functions

Sample Image - dragdroptree.jpg

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:

C++
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();
}

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:

C++
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);
}

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:

C++
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") ;
}

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.


Written By
Web Developer
United States United States
Dan is originally from (Philadelphia, PA) and now resides in Maryland. He was a philosophy major at Univ. of Maryland but has since graduated.
He also enjoys writing about himself in the 3rd person (like now).


Dan loves to travel and meet new people and experience different cultures. He plans on traveling to Europe in the coming months to backpack for 2 months.


Currently Dan is interested in CLisp, ATL, Java Servlets/JSP/EJB, C , COM/DCOM, MFC, API and of course Perl programming


My kickass website !


@{${$dan}{c}{clark}}[1].

Comments and Discussions

 
GeneralMy vote of 3 Pin
zerosprite21-Dec-13 1:50
zerosprite21-Dec-13 1:50 
GeneralFair play to you Pin
professore1-Nov-05 3:44
professore1-Nov-05 3:44 
QuestionAre you trying so hard? Pin
Anonymous7-Oct-05 0:38
Anonymous7-Oct-05 0:38 
GeneralHe loves himself too much Pin
Anonymous3-Jun-05 9:49
Anonymous3-Jun-05 9:49 
GeneralRe: He loves himself too much Pin
Dan Clark10-Aug-05 12:56
Dan Clark10-Aug-05 12:56 
GeneralHelp in cTree Background Pin
sirnoname2-Oct-04 13:12
sirnoname2-Oct-04 13:12 
GeneralRe: Help in cTree Background Pin
nikoladsp3-Jul-05 7:36
nikoladsp3-Jul-05 7:36 
GeneralUsing it as Managed C++ Pin
Alex Evans13-Mar-04 13:18
Alex Evans13-Mar-04 13:18 
GeneralFix for CTreeCtrl in FormView in SDI Pin
braddenn4-Feb-04 9:52
braddenn4-Feb-04 9:52 
GeneralThanks a lot! Pin
yelenka30-Oct-03 8:25
yelenka30-Oct-03 8:25 
GeneralRe: Thanks a lot! Pin
Dan Clark30-Oct-03 8:29
Dan Clark30-Oct-03 8:29 
GeneralSome strange things here ^^ Pin
guside24-Sep-03 20:35
guside24-Sep-03 20:35 
Questionhow unicode? Pin
kimcs23-Aug-03 5:21
kimcs23-Aug-03 5:21 
Generalthis is not your code man! Pin
digeradu30-Jul-03 5:49
sussdigeradu30-Jul-03 5:49 
GeneralRe: this is not your code man! Pin
ckatili30-Jul-03 6:03
ckatili30-Jul-03 6:03 
GeneralRe: this is not your code man! Who said it was moron! Pin
Dan Clark30-Jul-03 15:23
Dan Clark30-Jul-03 15:23 
GeneralCool off Pin
kamocio10-May-04 8:23
kamocio10-May-04 8:23 
GeneralRe: Cool off Pin
Dan Clark10-May-04 9:55
Dan Clark10-May-04 9:55 
GeneralDoesn't Work correctly if Image is smaller than viewable area Pin
IbanTheGreat12-Apr-03 13:04
IbanTheGreat12-Apr-03 13:04 
GeneralRe: Doesn't Work correctly if Image is smaller than viewable area Pin
Anonymous24-Jul-03 10:01
Anonymous24-Jul-03 10:01 
GeneralNot working on Win2000 Pin
Eric Forget27-Feb-03 8:27
Eric Forget27-Feb-03 8:27 
GeneralRe: Not working on Win2000 Pin
Dan Clark1-Mar-03 7:20
Dan Clark1-Mar-03 7:20 
GeneralRe: Not working on Win2000 Pin
C++ Matter14-Jun-06 11:22
C++ Matter14-Jun-06 11:22 
GeneralRe: Not working on Win2000 Pin
Anonymous20-May-03 10:39
Anonymous20-May-03 10:39 
GeneralDan Clark Dan Clark Pin
Andreas Saurwein16-Jan-03 23:38
Andreas Saurwein16-Jan-03 23:38 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.