Click here to Skip to main content
15,886,799 members
Please Sign up or sign in to vote.
4.00/5 (1 vote)
See more:
In this code I can drag and drop nodes and change the nodes places.

The problem is that if I drag a child node at any depth level it will drop the node/s at the top root level 0.

I need some how to keep the nodes depth level so if I'm dragging a node at depth level 3 for example so the node should move at the indexs of level 3 and not level 0.

But how do I tell it to drag and drop at the node level ?

C#
<pre>using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;

public class AdvancedTreeView : TreeView
{
    private Bitmap openedIcon, closedIcon;
    private List<TreeNode> rootNodes = new List<TreeNode>();

    public AdvancedTreeView()
    {
        DrawMode = TreeViewDrawMode.OwnerDrawText;
        ShowLines = false;
        AlternateBackColor = BackColor;
        ArrowColor = SystemColors.WindowText;
        this.AllowDrop = true;
    }

    public Color AlternateBackColor { get; set; }
    public Color ArrowColor { get; set; }

    protected override void OnDrawNode(DrawTreeNodeEventArgs e)
    {
        // background
        Color backColor = (GetTopNodeIndex(e.Node) & 1) == 0 ? BackColor : AlternateBackColor;
        using (Brush b = new SolidBrush(backColor))
        {
            e.Graphics.FillRectangle(b, new Rectangle(0, e.Bounds.Top, ClientSize.Width, e.Bounds.Height));
        }

        // icon
        if (e.Node.Nodes.Count > 0)
        {
            Image icon = GetIcon(e.Node.IsExpanded);
            e.Graphics.DrawImage(icon, e.Bounds.Left - icon.Width - 3, e.Bounds.Top);
        }

        // text (due to OwnerDrawText mode, indenting of e.Bounds will be correct)
        TextRenderer.DrawText(e.Graphics, e.Node.Text, Font, e.Bounds, ForeColor);

        // indicate selection (if not by backColor):
        if ((e.State & TreeNodeStates.Selected) != 0)
            ControlPaint.DrawFocusRectangle(e.Graphics, e.Bounds);
    }

    protected override void OnItemDrag(ItemDragEventArgs e)
    {
        // Move the dragged node when the left mouse button is used.
        if (e.Button == MouseButtons.Left)
        {
            DoDragDrop(e.Item, DragDropEffects.Move);
        }

        // Copy the dragged node when the right mouse button is used.
        else if (e.Button == MouseButtons.Right)
        {
            DoDragDrop(e.Item, DragDropEffects.Copy);
        } 
    }

    protected override void OnDragEnter(DragEventArgs e)
    {
        e.Effect = e.AllowedEffect;
    }

    protected override void OnDragOver(DragEventArgs e)
    {
        // Retrieve the client coordinates of the mouse position.
        Point targetPoint = this.PointToClient(new Point(e.X, e.Y));

        // Select the node at the mouse position.
        this.SelectedNode = this.GetNodeAt(targetPoint);
    }

    protected override void OnDragDrop(DragEventArgs e)
    {
        // Retrieve the client coordinates of the drop location.
        Point targetPoint = this.PointToClient(new Point(e.X, e.Y));

        // Retrieve the node at the drop location.
        TreeNode targetNode = this.GetNodeAt(targetPoint);

        // Retrieve the node that was dragged.
        TreeNode draggedNode = (TreeNode)e.Data.GetData(typeof(TreeNode));

        //draggedNode.Level;

        // Confirm that the node at the drop location is not 
        // the dragged node or a descendant of the dragged node.
        if (!draggedNode.Equals(targetNode))// && !ContainsNode(draggedNode, targetNode))
        {
            // If it is a move operation, remove the node from its current 
            // location and add it to the node at the drop location.
            if (e.Effect == DragDropEffects.Move)
            {
                // To check nad make that the indexs if the dragged and target nodes
                // Are childs to get the index of them in the place they are in the tree level
                // Now the indexs are belong to the root to the top of the tree.
                // So if a child index is 2 or 0 it will remove it and make the switch at the
                // Top level depth root. Instead where the childs are.
                int draggedLevel = draggedNode.Level;
                int targetLevel = targetNode.Level;
                int draggedindex = draggedNode.Index;
                int targetindex = targetNode.Index;

                draggedNode.Remove();
                targetNode.Remove();
                this.Nodes.Insert(targetindex, draggedNode);
                this.Nodes.Insert(draggedindex, targetNode);
            }

            // If it is a copy operation, clone the dragged node 
            // and add it to the node at the drop location.
            else if (e.Effect == DragDropEffects.Copy)
            {
                targetNode.Nodes.Add((TreeNode)draggedNode.Clone());
            }
        }
    }

    private int GetTopNodeIndex(TreeNode node)
    {
        while (node.Parent != null)
            node = node.Parent;

        return Nodes.IndexOf(node);
    }

    private Image GetIcon(bool nodeIsExpanded)
    {
        if (openedIcon == null)
            InitIcons();
        return nodeIsExpanded ? openedIcon : closedIcon;
    }

    private void InitIcons()
    {
        openedIcon = new Bitmap(16, 16);
        closedIcon = new Bitmap(16, 16);
        using (Brush b = new SolidBrush(ArrowColor))
        {
            using (Graphics g = Graphics.FromImage(openedIcon))
                g.FillPolygon(b, new[] { new Point(0, 0), new Point(15, 0), new Point(8, 15), });
            using (Graphics g = Graphics.FromImage(closedIcon))
                g.FillPolygon(b, new[] { new Point(0, 0), new Point(15, 8), new Point(0, 15), });
        }
    }
}


The part the make the drag and drop is inside OnDragDrop :

C#
int draggedindex = draggedNode.Index;
int targetindex = targetNode.Index;
draggedNode.Remove();
targetNode.Remove();
this.Nodes.Insert(targetindex, draggedNode);
this.Nodes.Insert(draggedindex, targetNode);


But again it will drop the dragged node and the target node to the level 0 even if I dragged a node at level 3 since it's thinking the indexes are of level 0. I don't know how to tell it to drag in the index of the level the nodes is.

This code in form1 is for testing I did it to see all the nodes depth levels :

C#
<pre>using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Treeview_Test
{
    public partial class Form1 : Form
    {
        List<int> levels = new List<int>();

        public Form1()
        {
            InitializeComponent();

            foreach (TreeNode node in advancedTreeView1.Nodes)
            {
                ListNodes(node);
            }
        }

       
        void ListNodes(TreeNode node)
        {
            foreach (TreeNode subnode in node.Nodes)
            {
                ListNodes(subnode);
            }

            levels.Add(node.Level);
        }
    }
}


So now I know for example that in the List levels index 0 is at level 0 and index 1 at level 4 index 2 at level 0 index 3 at level 2 index 4 at level 3 and so on.

But I still can't figure out how to make it in the OnDragDrop ?

What I have tried:

What have I tried is not much since I can't figure out what to do.
Posted
Updated 21-Jul-19 19:49pm
Comments
BillWoodruff 22-Jul-19 0:51am    
"so if I'm dragging a node at depth level 3 for example so the node should move at the indexs of level 3 and not level 0." I'm not clear what this means.

Do you mean you want the dropped Node to become a child node of the Node it is dropped on ?

Or, do you mean something else ?

1 solution

Look at what you are doing, now, when you drop during a move:
int draggedLevel = draggedNode.Level;
int targetLevel = targetNode.Level;
int draggedindex = draggedNode.Index;
int targetindex = targetNode.Index;

draggedNode.Remove();
targetNode.Remove();
treeView1.Nodes.Insert(targetindex, draggedNode);
treeView1.Nodes.Insert(draggedindex, targetNode);
1 while this code makes no use of the Level information, what is your intent in recording that information ?

2 both 'Inserts are into the Node collection of the Tree itself (root level).

3 after you call 'Remove on a Node, that Node is still "there," but it has no "Parent," or "Index:" you need that information to know where to insert the dropped Node.

4 Your code will throw an error if you drop on a non-node area of the TreeView" to handle that you need to handle 'targetNode == null

Try this:
int draggedindex = draggedNode.Index;
int targetindex = targetNode.Index;

bool isTargetNodeRoot = targetNode.Parent == null;

draggedNode.Remove();

if (isTargetNodeRoot)
{
    targetNode.Remove();

    treeView1.Nodes.Insert(targetindex, draggedNode);
    treeView1.Nodes.Insert(targetindex, targetNode);
}
else
{
    TreeNodeCollection nodes = targetNode.Parent.Nodes;
    targetNode.Remove();

    nodes.Insert(targetindex, draggedNode);
    nodes.Insert(targetindex, targetNode);
}
Note: Why do you think you need to remove the target drop Node, and, then, add it back in ?
 
Share this answer
 
v3

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900