Click here to Skip to main content
15,891,529 members
Articles / Desktop Programming / Windows Forms

Dragging tree nodes in C#

Rate me:
Please Sign up or sign in to vote.
4.83/5 (70 votes)
23 Jan 20054 min read 395.2K   9.6K   201  
This article shows how to implement an Explorer like treeview drag and drop in C#.
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;

namespace TreeViewDragDrop
{
	/// <summary>
	/// Summary description for Form1.
	/// </summary>
	public class Form1 : System.Windows.Forms.Form
	{
		// Node being dragged
		private TreeNode dragNode = null;
		
		// Temporary drop node for selection
		private TreeNode tempDropNode = null;

		// Timer for scrolling
		private Timer timer = new Timer();

		private System.ComponentModel.IContainer components;
		private System.Windows.Forms.ImageList imageListDrag;
		private System.Windows.Forms.ImageList imageListTreeView;
		private System.Windows.Forms.TreeView treeView1;


		public Form1()
		{
			InitializeComponent();

			this.treeView1.ExpandAll();

			timer.Interval = 200;
			timer.Tick += new EventHandler(timer_Tick);
		}

		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if (components != null) 
				{
					components.Dispose();
				}
			}
			base.Dispose( disposing );
		}

		#region Windows Form Designer generated code
		/// <summary>
		/// Required method for Designer support - do not modify
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
			this.components = new System.ComponentModel.Container();
			System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(Form1));
			this.imageListDrag = new System.Windows.Forms.ImageList(this.components);
			this.treeView1 = new System.Windows.Forms.TreeView();
			this.imageListTreeView = new System.Windows.Forms.ImageList(this.components);
			this.SuspendLayout();
			// 
			// imageListDrag
			// 
			this.imageListDrag.ColorDepth = System.Windows.Forms.ColorDepth.Depth32Bit;
			this.imageListDrag.ImageSize = new System.Drawing.Size(16, 16);
			this.imageListDrag.TransparentColor = System.Drawing.Color.Transparent;
			// 
			// treeView1
			// 
			this.treeView1.AllowDrop = true;
			this.treeView1.Dock = System.Windows.Forms.DockStyle.Fill;
			this.treeView1.ImageList = this.imageListTreeView;
			this.treeView1.Indent = 19;
			this.treeView1.Location = new System.Drawing.Point(0, 0);
			this.treeView1.Name = "treeView1";
			this.treeView1.Nodes.AddRange(new System.Windows.Forms.TreeNode[] {
																				  new System.Windows.Forms.TreeNode("Node0", new System.Windows.Forms.TreeNode[] {
																																									 new System.Windows.Forms.TreeNode("Node1", new System.Windows.Forms.TreeNode[] {
																																																														new System.Windows.Forms.TreeNode("Node2", new System.Windows.Forms.TreeNode[] {
																																																																																		   new System.Windows.Forms.TreeNode("Node7")}),
																																																														new System.Windows.Forms.TreeNode("Node5"),
																																																														new System.Windows.Forms.TreeNode("Node6")})}),
																				  new System.Windows.Forms.TreeNode("Node3", new System.Windows.Forms.TreeNode[] {
																																									 new System.Windows.Forms.TreeNode("Node8"),
																																									 new System.Windows.Forms.TreeNode("Node9"),
																																									 new System.Windows.Forms.TreeNode("Node10")}),
																				  new System.Windows.Forms.TreeNode("Node4"),
																				  new System.Windows.Forms.TreeNode("Node11", new System.Windows.Forms.TreeNode[] {
																																									  new System.Windows.Forms.TreeNode("Node0"),
																																									  new System.Windows.Forms.TreeNode("Node1"),
																																									  new System.Windows.Forms.TreeNode("Node2"),
																																									  new System.Windows.Forms.TreeNode("Node3"),
																																									  new System.Windows.Forms.TreeNode("Node4"),
																																									  new System.Windows.Forms.TreeNode("Node5"),
																																									  new System.Windows.Forms.TreeNode("Node6"),
																																									  new System.Windows.Forms.TreeNode("Node7")})});
			this.treeView1.Size = new System.Drawing.Size(272, 237);
			this.treeView1.TabIndex = 0;
			this.treeView1.DragOver += new System.Windows.Forms.DragEventHandler(this.treeView1_DragOver);
			this.treeView1.DragEnter += new System.Windows.Forms.DragEventHandler(this.treeView1_DragEnter);
			this.treeView1.ItemDrag += new System.Windows.Forms.ItemDragEventHandler(this.treeView_ItemDrag);
			this.treeView1.DragLeave += new System.EventHandler(this.treeView1_DragLeave);
			this.treeView1.DragDrop += new System.Windows.Forms.DragEventHandler(this.treeView1_DragDrop);
			this.treeView1.GiveFeedback += new System.Windows.Forms.GiveFeedbackEventHandler(this.treeView1_GiveFeedback);
			// 
			// imageListTreeView
			// 
			this.imageListTreeView.ColorDepth = System.Windows.Forms.ColorDepth.Depth32Bit;
			this.imageListTreeView.ImageSize = new System.Drawing.Size(16, 16);
			this.imageListTreeView.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("imageListTreeView.ImageStream")));
			this.imageListTreeView.TransparentColor = System.Drawing.Color.Transparent;
			// 
			// Form1
			// 
			this.AllowDrop = true;
			this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
			this.ClientSize = new System.Drawing.Size(272, 237);
			this.Controls.Add(this.treeView1);
			this.MaximizeBox = false;
			this.MinimizeBox = false;
			this.Name = "Form1";
			this.Text = "Tree View Drag & Drop";
			this.ResumeLayout(false);

		}
		#endregion

		/// <summary>
		/// The main entry point for the application.
		/// </summary>
		[STAThread]
		static void Main() 
		{
			Application.Run(new Form1());
		}


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

		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);

			// Get actual drop node
			TreeNode dropNode = this.treeView1.GetNodeAt(this.treeView1.PointToClient(new Point(e.X, e.Y)));
			if(dropNode == null)
			{
				e.Effect = DragDropEffects.None;
				return;
			}
			
			e.Effect = DragDropEffects.Move;

			// if mouse is on a new node select it
			if(this.tempDropNode != dropNode)
			{
				DragHelper.ImageList_DragShowNolock(false);
				this.treeView1.SelectedNode = dropNode;
				DragHelper.ImageList_DragShowNolock(true);
				tempDropNode = dropNode;
			}
			
			// Avoid that drop node is child of drag node 
			TreeNode tmpNode = dropNode;
			while(tmpNode.Parent != null)
			{
				if(tmpNode.Parent == this.dragNode) e.Effect = DragDropEffects.None;
				tmpNode = tmpNode.Parent;
			}
		}

		private void treeView1_DragDrop(object sender, System.Windows.Forms.DragEventArgs e)
		{
			// Unlock updates
			DragHelper.ImageList_DragLeave(this.treeView1.Handle);

			// Get drop node
			TreeNode dropNode = this.treeView1.GetNodeAt(this.treeView1.PointToClient(new Point(e.X, e.Y)));

			// If drop node isn't equal to drag node, add drag node as child of drop node
			if(this.dragNode != dropNode)
			{
				// Remove drag node from parent
				if(this.dragNode.Parent == null)
				{
					this.treeView1.Nodes.Remove(this.dragNode);
				}
				else
				{
					this.dragNode.Parent.Nodes.Remove(this.dragNode);
				}

				// Add drag node to drop node
				dropNode.Nodes.Add(this.dragNode);
				dropNode.ExpandAll();

				// Set drag node to null
				this.dragNode = null;

				// Disable scroll timer
				this.timer.Enabled = false;
			}
		}

		private void treeView1_DragEnter(object sender, System.Windows.Forms.DragEventArgs e)
		{
			DragHelper.ImageList_DragEnter(this.treeView1.Handle, e.X - this.treeView1.Left,
				e.Y - this.treeView1.Top);

			// Enable timer for scrolling dragged item
			this.timer.Enabled = true;
		}

		private void treeView1_DragLeave(object sender, System.EventArgs e)
		{
			DragHelper.ImageList_DragLeave(this.treeView1.Handle);

			// Disable timer for scrolling dragged item
			this.timer.Enabled = false;
		}

		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;
			
		}

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

	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

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
Switzerland Switzerland
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions