Click here to Skip to main content
11,637,176 members (60,530 online)
Click here to Skip to main content

TreeView Rearrange

, 6 Jun 2005 Public Domain 170.9K 2.4K 125
Rate this:
Please Sign up or sign in to vote.
How to do a TreeView rearrange.

Introduction

While merrily programming a top secret (snicker) application I was developing - I ran into a major roadblock that set me back a few days. My application was based around a treeview but I wanted to give users the ability to rearrange the node/folder structure of the treeview using drag and drop. Upon a bit of research, I found the treeview does have some sort of drag & drop support, but as far as I could tell, this just turned on the event handlers for when a user starts dragging a node. "Hmmm", I thought, "this is gonna require some custom coding."... Let's delve into my series of failures and successes leading us to a workable extension of the treeview control.

How is it going to work?

In my first attempt, I wanted all the visual goodies. I wanted the dragged node to be placed into the "slots" in between nodes as I moved over them. This, unfortunately, led me to some problems. When the node moves into a slot - the treeview structure changes and the node your mouse is currently over changes entirely, this introduced some (only slightly entertaining) node dances. I then decided to remove the node being dragged, to prevent the node dance - and it worked! My next problem was that I wanted to be able to drag the node into a "folder" node but what if the folder node is closed? I'm sure you guessed it, use Expand()! But guess what!? The dreaded node dance returned! :(

I gave up for the night and went to bed without any hope and (like many a times) woke up with an answer. Mimic the "Windows" (tm) way and implement a placeholder letting the user know where the item will be dropped. With a bit of work and a few cases of RC cola, I had the functionality I wanted.

Moving on to the code

Most of the functionality is handled within the "DragOver" event of the treeview control, however there are two other events that needed to be handled to give you the cursor effect you need.

private void treeView1_ItemDrag(object sender, 
  System.Windows.Forms.ItemDragEventArgs e)
{
    DoDragDrop(e.Item, DragDropEffects.Move);
}


private void treeView1_DragEnter(object sender, 
  System.Windows.Forms.DragEventArgs e)
{
    e.Effect = DragDropEffects.Move;
}

Now into the DragOver event. The point to remember here is that we're dealing with two types of nodes: those that can accept children and those that can't. For nodes that can accept children, we need to have three vertical regions we have to watch for:

  • Top (placeholder above)
  • Bottom (placeholder below)
  • Middle (expand the folder and allow users to drop nodes onto it)

The non-folder nodes are divided into two vertical regions. For brevity, I will only go into the code for the "top" of the "non-folder" node. This code is divided into four main parts:

  • Paradox prevention, we should never be allowed to drag a folder into a child folder.
  • Store the current placeholder into a global string called NodeMap, basically a pipe delimited set of indexes. If the new NodeMap matches the global NodeMap then method returns to minimize flickering.
  • Refresh the screen to remove old placeholders.
  • Draw the placeholders.
#region If NodeOver is a child then cancel
TreeNode tnParadox = NodeOver;
while(tnParadox.Parent != null)
{
    if(tnParadox.Parent == NodeMoving)
    {
        this.NodeMap = "";
        return;
    }
    
    tnParadox = tnParadox.Parent;
}
#endregion
#region Store the placeholder info into a pipe delimited string
TreeNode tnPlaceholderInfo = NodeOver;
string NewNodeMap = ((int)NodeOver.Index).ToString();
while(tnPlaceholderInfo.Parent != null)
{
    tnPlaceholderInfo = tnPlaceholderInfo.Parent;
     NewNodeMap = tnPlaceholderInfo.Index + "|" + NewNodeMap;
}
if(NewNodeMap == this.NodeMap)
    return;
else
    this.NodeMap = NewNodeMap;
#endregion
#region Clear placeholders above and below
this.Refresh();
#endregion
#region Draw the placeholders
int LeftPos, RightPos;
LeftPos = NodeOver.Bounds.Left - NodeOverImageWidth;
RightPos = this.treeView1.Width - 4;
Point[] LeftTriangle = new Point[5]{
    new Point(LeftPos, NodeOver.Bounds.Top - 4), 
    new Point(LeftPos, NodeOver.Bounds.Top + 4), 
    new Point(LeftPos + 4, NodeOver.Bounds.Y), 
    new Point(LeftPos + 4, NodeOver.Bounds.Top - 1), 
    new Point(LeftPos, NodeOver.Bounds.Top - 5)};

Point[] RightTriangle = new Point[5]{
    new Point(RightPos, NodeOver.Bounds.Top - 4),
    new Point(RightPos, NodeOver.Bounds.Top + 4),
    new Point(RightPos - 4, NodeOver.Bounds.Y),
    new Point(RightPos - 4, NodeOver.Bounds.Top - 1),
    new Point(RightPos, NodeOver.Bounds.Top - 5)};

g.FillPolygon(System.Drawing.Brushes.Black, LeftTriangle);
g.FillPolygon(System.Drawing.Brushes.Black, RightTriangle);
g.DrawLine(new System.Drawing.Pen(Color.Black, 2), 
  new Point(LeftPos, NodeOver.Bounds.Top), 
  new Point(RightPos, NodeOver.Bounds.Top));
#endregion

The placeholders are drawn with three graphics calls to create the black line and two triangles.

The final piece to the puzzle is actually moving the dragged node. This is handled in the drag_drop event. It basically traverses the nodemap, adds the new node and removes the old one.

private void treeView1_DragDrop(object sender, 
  System.Windows.Forms.DragEventArgs e)

{
    if(e.Data.GetDataPresent("System.Windows.Forms.TreeNode",
      false) && this.NodeMap != "")
    { 
        TreeNode MovingNode = (TreeNode)e.Data.GetData(
          "System.Windows.Forms.TreeNode");
        string[] NodeIndexes = this.NodeMap.Split('|');
        TreeNodeCollection InsertCollection = this.treeView1.Nodes;
        for(int i = 0; i < NodeIndexes.Length - 1; i++)
        {
            InsertCollection = InsertCollection[Int32.Parse(
               NodeIndexes[i])].Nodes;
        }
        if(InsertCollection != null)
        {
            InsertCollection.Insert(Int32.Parse(NodeIndexes[
             NodeIndexes.Length - 1]), (TreeNode)MovingNode.Clone());
            this.treeView1.SelectedNode = InsertCollection[
             Int32.Parse(NodeIndexes[NodeIndexes.Length - 1])];
            MovingNode.Remove();
        }
    } 

}

Remarks

While not a completely comprehensive solution, I believe this article will be a good stepping stone for those wanting to add better drag and drop support to their applications. The code is divided clearly into regions and commented fairly well.

I look forward to your comments and suggestions. CodeProject is a great site, and I'm glad to contribute.

History

  • February 11, 2004: (Version 1.0) code and article submitted.
  • March 7, 2004: (Version 1.1) minor bug where the last subfolder could be dragged onto itself fixed.
  • June, 5, 2005 (Version 1.2) utilized code by creatio, needed some minor tweaks but got it working. Also made an attempt to convert the code into VB.NET - there is a problem with the code - see comment that says "Error!". Not sure what is going on because I'm not a very competent VB.NET developer - advice appreciated.

License

This article, along with any associated source code and files, is licensed under A Public Domain dedication

Share

About the Author

Gabe Anguiano
United States United States
No Biography provided

You may also be interested in...

Comments and Discussions

 
QuestionLicense Pin
splashtech21-Oct-13 8:44
membersplashtech21-Oct-13 8:44 
AnswerRe: License Pin
Gabe Anguiano21-Oct-13 14:57
memberGabe Anguiano21-Oct-13 14:57 
GeneralRe: License Pin
splashtech22-Oct-13 8:48
membersplashtech22-Oct-13 8:48 
GeneralRe: License Pin
Gabe Anguiano23-Oct-13 4:29
memberGabe Anguiano23-Oct-13 4:29 
GeneralMy vote of 5 Pin
Dirkus Maximus30-Mar-12 22:18
memberDirkus Maximus30-Mar-12 22:18 
QuestionGreat job!! Pin
Dirkus Maximus30-Mar-12 0:46
memberDirkus Maximus30-Mar-12 0:46 
Questionhelpful for me Pin
jonred018-Oct-11 11:13
memberjonred018-Oct-11 11:13 
GeneralVery useful, thanks Pin
Guillermo Toro31-Jan-11 3:54
memberGuillermo Toro31-Jan-11 3:54 
GeneralMy vote of 5 Pin
Guillermo Toro31-Jan-11 3:52
memberGuillermo Toro31-Jan-11 3:52 
GeneralC# To VB.NET (and vice versa) Converter Website Pin
Zac Greve13-Jul-09 16:22
memberZac Greve13-Jul-09 16:22 
GeneralFix for VB error you mentioned in June 2005 History Pin
Don Meuse24-Apr-08 8:05
memberDon Meuse24-Apr-08 8:05 
GeneralRe: Fix for VB error you mentioned in June 2005 History Pin
[CC]28-Aug-08 2:12
member[CC]28-Aug-08 2:12 
GeneralMy first message Pin
gwisertal3-Jul-07 20:40
membergwisertal3-Jul-07 20:40 
QuestionHow do I drag from outside and into the view? Pin
Mads19674-Jun-07 0:00
memberMads19674-Jun-07 0:00 
GeneralSome comments Pin
Marc Clifton28-May-06 3:23
protectorMarc Clifton28-May-06 3:23 
GeneralRe: Some comments Pin
Gabe Anguiano30-May-06 16:47
memberGabe Anguiano30-May-06 16:47 
GeneralRe: Some comments Pin
Marc Clifton30-May-06 17:03
protectorMarc Clifton30-May-06 17:03 
GeneralWorst code ever... Pin
zavitax23-Nov-05 3:35
memberzavitax23-Nov-05 3:35 
GeneralRe: Worst code ever... Pin
Gabe Anguiano23-Nov-05 6:50
memberGabe Anguiano23-Nov-05 6:50 
GeneralRe: Worst code ever... Pin
zavitax24-Nov-05 10:21
memberzavitax24-Nov-05 10:21 
GeneralRe: Worst code ever... Pin
f27-Jul-07 21:27
memberf27-Jul-07 21:27 
GeneralRe: Worst code ever... [modified] Pin
Grafalgar12-Oct-09 14:49
memberGrafalgar12-Oct-09 14:49 
GeneralRe: Worst code ever... Pin
f217-Oct-09 23:06
memberf217-Oct-09 23:06 
GeneralRe: Worst code ever... Pin
Omnicoder18-Dec-09 17:56
memberOmnicoder18-Dec-09 17:56 
GeneralGreat, it even works with multiple TreeViews Pin
Chals15-Sep-05 22:49
memberChals15-Sep-05 22:49 
GeneralRe: Great, it even works with multiple TreeViews Pin
Gabe Anguiano16-Sep-05 7:55
memberGabe Anguiano16-Sep-05 7:55 
QuestionHow can I darg-drop item to a WebBrowser control ? Pin
Anonymous3-Sep-05 15:44
sussAnonymous3-Sep-05 15:44 
AnswerRe: How can I darg-drop item to a WebBrowser control ? Pin
Gabe Anguiano16-Sep-05 7:53
memberGabe Anguiano16-Sep-05 7:53 
GeneralPossible in VC++ Pin
smile869123-Aug-05 9:45
membersmile869123-Aug-05 9:45 
GeneralRe: Possible in VC++ Pin
Gabe Anguiano23-Aug-05 9:46
memberGabe Anguiano23-Aug-05 9:46 
GeneralRe: Possible in VC++ Pin
smile869123-Aug-05 9:48
membersmile869123-Aug-05 9:48 
GeneralTreeView Rearrange Pin
smile86919-Aug-05 4:27
membersmile86919-Aug-05 4:27 
GeneralNice code sample Pin
Emiliano9-Jun-05 1:48
memberEmiliano9-Jun-05 1:48 
GeneralRe: Nice code sample Pin
Gabe Anguiano9-Jun-05 5:55
memberGabe Anguiano9-Jun-05 5:55 
GeneralSome notes. Pin
creatio26-May-05 0:03
membercreatio26-May-05 0:03 
GeneralRe: Some notes. Pin
Gabe Anguiano26-May-05 5:32
memberGabe Anguiano26-May-05 5:32 
GeneralRe: Some notes. Pin
creatio26-May-05 6:08
membercreatio26-May-05 6:08 
GeneralRe: Some notes. Pin
B. ten Hoopen21-Apr-06 0:26
memberB. ten Hoopen21-Apr-06 0:26 
QuestionRe: Some notes. [modified] Pin
marcogaribay7-Jun-06 7:16
membermarcogaribay7-Jun-06 7:16 
AnswerRe: Some notes. [modified] Pin
B. ten Hoopen7-Jun-06 23:22
memberB. ten Hoopen7-Jun-06 23:22 
GeneralAquire the target node Pin
larsiiii16-May-05 23:15
memberlarsiiii16-May-05 23:15 
GeneralRe: Aquire the target node Pin
Gabe Anguiano5-Jun-05 10:45
memberGabe Anguiano5-Jun-05 10:45 
GeneralRe: Aquire the target node Pin
Gabe Anguiano5-Jun-05 10:46
memberGabe Anguiano5-Jun-05 10:46 
GeneralVisual Basic Pin
bharatthakkar4-May-05 22:19
memberbharatthakkar4-May-05 22:19 
GeneralRe: Visual Basic Pin
Gabe Anguiano5-Jun-05 12:48
memberGabe Anguiano5-Jun-05 12:48 
GeneralRe: Visual Basic Pin
bharatthakkar5-Jun-05 12:52
memberbharatthakkar5-Jun-05 12:52 
GeneralThanks Pin
prog329-Mar-05 4:27
memberprog329-Mar-05 4:27 
GeneralRe: Thanks Pin
Gabe Anguiano11-Mar-05 7:50
memberGabe Anguiano11-Mar-05 7:50 
QuestionNice .. Possible in VB? Pin
faluette4-Nov-04 14:35
memberfaluette4-Nov-04 14:35 
AnswerYes - here is a conversion to VB Pin
Clark Sann25-Nov-04 8:19
memberClark Sann25-Nov-04 8:19 

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

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

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.150728.1 | Last Updated 6 Jun 2005
Article Copyright 2004 by Gabe Anguiano
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid