Click here to Skip to main content
6,594,932 members and growing! (15,534 online)
Email Password   helpLost your password?
Desktop Development » Tree Controls » TreeView Controls     Intermediate

Dragging tree nodes in C#

By Michea77

This article shows how to implement an Explorer like treeview drag and drop in C#.
C#, Windows, .NET 1.1, GDI+, WinForms, VS.NET2003, Dev
Posted:23 Jan 2005
Views:142,746
Bookmarked:156 times
Announcements
Loading...
 
Search    
Advanced Search
Add to IE Search
printPrint   add Share
      Discuss Discuss   Broken Article?Report  
48 votes for this article.
Popularity: 8.03 Rating: 4.78 out of 5

1

2

3
8 votes, 16.7%
4
40 votes, 83.3%
5

TreeView Drag & Drop

Introduction

During the implementation of a C# application, I was faced with the problem of adding drag and drop functionality to a tree view. So far no problems, but in order to make the whole a bit fancier, I decided to add image dragging too, as it is done by the Windows Explorer when dragging files or directories. Here I got into troubles, since image dragging isn't supported by the .NET controls. Not finding satisfactory code in the Internet (maybe I'm not a good surfer...), I decided to try it by myself. Having fun on it, I improved the code by adding scrolling to the component so that a dragged element can be dropped everywhere on the control.

This article will only briefly describe the basics of drag and drop, as it is quite straightforward to implement and there already are a lot of good articles about it. The aim of the article is to describe how image dragging and automatic scrolling while dragging can be implemented in C#.

TreeView Drag and Drop

In order to allow Drag and Drop on a TreeView, the AllowDrop flag must be set and handlers for the the following events (or some of them) must be implemented:

  • ItemDrag - This event is fired as soon as a drag operation is started. This event is specific for listviews and treeviews. The dragged element is passed as an argument of the event. The handler of this event should contain the DoDragDrop() call that begins the drag and drop operation.
  • DragOver - This event is fired when the user drags over a drag and drop control with the mouse.
  • DragEnter - This event is fired when the user moves the mouse onto the control while dragging an element.
  • DragLeave - This event is fired when the user leaves the control with the mouse while dragging an element.
  • DragDrop - This event is fired when the user releases the mouse over the drop target.
  • GiveFeedback - This event gives feedback about the current drag effect and cursor.

Image dragging

The implementation of image dragging requires functionalities that first of all create a ghost image of the dragging element and then move this image as mouse cursor moves over the TreeView control. Part of the needed functionalities are available in the ImageList implementation of Win32 (WinAPI). In order to call these functions, I wrote the class DragHelper that accesses them via P/Invoke.

public class DragHelper
{
    [DllImport("comctl32.dll")]
    public static extern bool InitCommonControls();

    [DllImport("comctl32.dll", CharSet=CharSet.Auto)]
    public static extern bool ImageList_BeginDrag(
        IntPtr himlTrack, // Handler of the image list containing the image to drag

        int iTrack,       // Index of the image to drag 

        int dxHotspot,    // x-delta between mouse position and drag image

        int dyHotspot     // y-delta between mouse position and drag image

    );

    [DllImport("comctl32.dll", CharSet=CharSet.Auto)]
    public static extern bool ImageList_DragMove(
        int x,   // X-coordinate (relative to the form,

                 // not the treeview) at which to display the drag image.

        int y,   // Y-coordinate (relative to the form,

                 // not the treeview) at which to display the drag image.

    );

    [DllImport("comctl32.dll", CharSet=CharSet.Auto)]
    public static extern void ImageList_EndDrag();

    [DllImport("comctl32.dll", CharSet=CharSet.Auto)]
    public static extern bool ImageList_DragEnter(
        IntPtr hwndLock,  // Handle to the control that owns the drag image.

        int x,            // X-coordinate (relative to the treeview)

                          // at which to display the drag image. 

        int y             // Y-coordinate (relative to the treeview)

                          // at which to display the drag image. 

    );

    [DllImport("comctl32.dll", CharSet=CharSet.Auto)]
    public static extern bool ImageList_DragLeave(
        IntPtr hwndLock  // Handle to the control that owns the drag image.

    );

    [DllImport("comctl32.dll", CharSet=CharSet.Auto)]
    public static extern bool ImageList_DragShowNolock(
        bool fShow       // False to hide, true to show the image

    );

    static DragHelper()
    {
        InitCommonControls();
    }
}

The first thing to do when we start dragging an element is to create the ghost image of the tree node. Help is provided by the function ImageList_BeginDrag which creates for us a ghost image. This function needs as parameter the handler of an ImageList with the image to be made transparent in it. To create the image of the tree node to drag, a new bitmap is created and the icon and the label are drawn in it. At the end of the dragging operation, the ghost image is destroyed by a call to the ImageList_EndDrag function. We do all this in the ItemDrag event handler.

private void treeView_ItemDrag(object sender, 
                  System.Windows.Forms.ItemDragEventArgs e)
{
    // Get drag node and select it

    this.dragNode = (TreeNode)e.Item;
    this.treeView1.SelectedNode = this.dragNode;

    // Reset image list used for drag image

    this.imageListDrag.Images.Clear();
    this.imageListDrag.ImageSize = 
          new Size(this.dragNode.Bounds.Size.Width 
          + this.treeView1.Indent, this.dragNode.Bounds.Height);

    // Create new bitmap

    // This bitmap will contain the tree node image to be dragged

    Bitmap bmp = new Bitmap(this.dragNode.Bounds.Width 
        + this.treeView1.Indent, this.dragNode.Bounds.Height);

    // Get graphics from bitmap

    Graphics gfx = Graphics.FromImage(bmp);

    // Draw node icon into the bitmap

    gfx.DrawImage(this.imageListTreeView.Images[0], 0, 0);

    // Draw node label into bitmap

    gfx.DrawString(this.dragNode.Text,
        this.treeView1.Font,
        new SolidBrush(this.treeView1.ForeColor),
        (float)this.treeView1.Indent, 1.0f);

    // Add bitmap to imagelist

    this.imageListDrag.Images.Add(bmp);

    // Get mouse position in client coordinates

    Point p = this.treeView1.PointToClient(Control.MousePosition);

    // Compute delta between mouse position and node bounds

    int dx = p.X + this.treeView1.Indent - this.dragNode.Bounds.Left;
    int dy = p.Y - this.dragNode.Bounds.Top;

    // Begin dragging image

    if (DragHelper.ImageList_BeginDrag(this.imageListDrag.Handle, 0, dx, dy))
    {
        // Begin dragging

        this.treeView1.DoDragDrop(bmp, DragDropEffects.Move);
        // End dragging image

        DragHelper.ImageList_EndDrag();
    }

}

When the mouse is now moved while a tree node is dragged, the ghost image should follow the mouse cursor. This is done by the function ImageList_DragMove. We implement it in the DragOver event handler.

private void treeView1_DragOver(object sender, 
              System.Windows.Forms.DragEventArgs e)
{
    // Compute drag position and move image

    Point formP = this.PointToClient(new Point(e.X, e.Y));
    DragHelper.ImageList_DragMove(formP.X - this.treeView1.Left, 
                                  formP.Y - this.treeView1.Top);

    ...
}

If we leave the TreeView, the ghost image should disappear, and as soon as we re-enter the control, the image should appear again. This is done by the functions ImageList_DragLeave and ImageList_DragEnter. The function ImageList_DragEnter also locks the window for updates to allow a clean dragging of the image. ImageList_DragLeave respectively releases the update lock. We implement these two functions in the corresponding event handlers (treeView1_DragEnter and treeView1_DragLeave).

While an element is dragged, Windows automatically changes the mouse cursor according to the drag effect (copy, move, none, ...). In our example, we use the DragDropEffects.Move drag effect. The mouse cursor we want to have while dragging the ghost image is the normal pointer cursor. The cursor can be set in the GiveFeedback event handler.

private void treeView1_GiveFeedback(object sender, 
           System.Windows.Forms.GiveFeedbackEventArgs e)
{
    if(e.Effect == DragDropEffects.Move) 
    {
        // Show pointer cursor while dragging

        e.UseDefaultCursors = false;
        this.treeView1.Cursor = Cursors.Default;
    }
    else e.UseDefaultCursors = true;

}

As soon as the user drops the element and thus terminates the dragging operation, the control updates must be unlocked calling the function ImageList_DragLeave. The DoDragDrop() call (see treeView1_ItemDrag) terminates and the ghost image is released with ImageList_EndDrag.

private void treeView1_DragDrop(object sender, 
                  System.Windows.Forms.DragEventArgs e)
{
    // Unlock updates

    DragHelper.ImageList_DragLeave(this.treeView1.Handle);

    ...

    }
}

Scrolling while dragging

Without scrolling, we can not reach each node within a tree while we are dragging an element, unless the tree is entirely visible on the screen. We begin scrolling the control if the mouse cursor reaches the top or the bottom of the TreeView control. The scrolling is achieved through the EnsureVisible() method of the TreeNode class. To scroll up, we get the previous visible node through the property PrevVisibleNode of the TreeNode class, and set it visible. Analogously, we scroll down with the property NextVisibleNode and a call to EnsureVisible(). As soon as we begin dragging a node, a timer is started. At each tick of the timer, the current position of the cursor is checked, and if it is near the upper or lower border of the control, the tree is scrolled. To avoid that the dragging image interferes with the scrolling producing ugly graphical effects, we briefly hide the drag image and unlock the paint updates with the function ImageList_DragShowNolock. That's it!

private void timer_Tick(object sender, EventArgs e)
{
    // get node at mouse position

    Point pt = PointToClient(Control.MousePosition);
    TreeNode node = this.treeView1.GetNodeAt(pt);

    if(node == null) return;

    // if mouse is near to the top, scroll up

    if(pt.Y < 30)
    {
        // set actual node to the upper one

        if (node.PrevVisibleNode!= null) 
        {
            node = node.PrevVisibleNode;

            // hide drag image

            DragHelper.ImageList_DragShowNolock(false);
            // scroll and refresh

            node.EnsureVisible();
            this.treeView1.Refresh();
            // show drag image

            DragHelper.ImageList_DragShowNolock(true);

        }
    }
    // if mouse is near to the bottom, scroll down

    else if(pt.Y > this.treeView1.Size.Height - 30)
    {
        if (node.NextVisibleNode!= null) 
        {
            node = node.NextVisibleNode;

            DragHelper.ImageList_DragShowNolock(false);
            node.EnsureVisible();
            this.treeView1.Refresh();
            DragHelper.ImageList_DragShowNolock(true);
        }
    } 
}

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

About the Author

Michea77


Member

Occupation: Web Developer
Location: Switzerland Switzerland

Other popular Tree Controls articles:

Article Top
You must Sign In to use this message board.
FAQ FAQ 
 
Noise Tolerance  Layout  Per page   
 Msgs 1 to 25 of 59 (Total in Forum: 59) (Refresh)FirstPrevNext
Generalhow to show the ghost image when we leave the treeview? Pinmembercpw999cn22:43 11 Oct '09  
GeneralTree restrictions Pinmemberdede_dafreak1:09 23 Jun '09  
GeneralReording nodes PinmemberJimLaVine17:43 6 Jun '09  
GeneralRe: Reording nodes Pinmemberthebeard16:08 2 Jul '09  
GeneralGreat Work PinmemberRichard Blythe6:52 18 May '09  
GeneralCannot open project (VS05) PinmemberFransClasener11:14 8 Jun '08  
NewsGreat artical w/update to a small issue when using TreeViewDrawMode.OwnerDrawText PinmemberGreg Cadmes14:44 5 Mar '08  
GeneralVery good article. Thank you. PinmemberMuaddubby9:03 20 Feb '08  
QuestionHow can this be extended to enable Drag amnd Drop of multiple nodes. [modified] Pinmembervinutha kempanna8:04 20 Feb '08  
QuestionProblem in MdiChild form PinmemberDejan Stojanovic23:19 31 Oct '07  
GeneralSelectedNode trail problem with this code PinmemberRageKing13:36 29 Oct '07  
GeneralRe: SelectedNode trail problem with this code Pinmemberdarkzangel3:38 14 May '08  
GeneralNodes MouseHover Event . Pinmemberunitecsoft22:46 7 Aug '07  
Generalhow to highlights XtraTreelist nodes dragging from Grid PinmemberShantanu Behera22:27 17 Apr '07  
GeneralHow Can I Use It In VB2005? PinmemberICCI7:32 3 Apr '07  
GeneralNice job... Pinmembermatthias.sitte1:58 9 Mar '07  
QuestionHow can I drag between different windows? Pinmembersami ve susu6:05 24 Dec '06  
GeneralPerfect. Thank you PinmemberEugen Wiebe0:55 22 Nov '06  
QuestionCan we use drag&drop without using win32 API [modified] PinmemberAwaisAhmedKhan1:04 5 Jun '06  
GeneralGreat Job PinmemberCem Kalyoncu23:51 28 May '06  
GeneralThere is a small bug ~ Pinmemberjustin_wang0:00 27 Apr '06  
AnswerRe: There is a small bug ~ Pinmemberkalelpack11:24 26 Feb '08  
GeneralScrolling down Pinmemberboldtbanan11:12 30 Jan '06  
GeneralRe: Scrolling down Pinmembermmjd20:40 15 Feb '06  
GeneralRe: Scrolling down Pinmemberkalelpack5:43 15 Feb '08  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 23 Jan 2005
Editor: Smitha Vijayan
Copyright 2005 by Michea77
Everything else Copyright © CodeProject, 1999-2009
Web22 | Advertise on the Code Project