WinForms FAQ - TreeView

Find answers for the most frequently asked questions
Expand All Collapse All

In a posting in the Microsoft.Windows.Forms newsgroup, Brian Roder (Microsoft) gives VB.Net code snippets to handle the DragEnter, ItemDrag and DragDrop events that provide a solution to this problem. You can get C# code in this sample, TreeViewDnD. Here is some sample handlers.

private void treeView2_DragDrop(object sender, System.Windows.Forms.DragEventArgs e)
{
  TreeNode newNode;
  if( e.Data.GetDataPresent(''System.Windows.Forms.TreeNode'', false))
  {
    Point pt;
    TreeNode destinationNode;
    pt = treeView2.PointToClient(new Point(e.X, e.Y));
    destinationNode = treeView2.GetNodeAt(pt);
    newNode = (TreeNode) e.Data.GetData(''System.Windows.Forms.TreeNode'');
    if(!destinationNode.Equals(newNode))
    {
      //destinationNode.Nodes.Add(newNode.Clone());
      destinationNode.Nodes.Add((TreeNode) newNode.Clone());
      destinationNode.Expand();
      //Remove original node
      newNode.Remove();
    }
  }
}

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

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

You can display a context menu when a user right-clicks on a node by listening to the TreeView’s MouseUp event as shown below:

[C#]
private void treeView1_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
{  
	if(e.Button == MouseButtons.Right)  
	{   
		Point ClickPoint = new Point(e.X,e.Y);   
		TreeNode ClickNode = treeView1.GetNodeAt(ClickPoint);   
		if(ClickNode == null) return;   
		// Convert from Tree coordinates to Screen coordinates    
		Point ScreenPoint = treeView1.PointToScreen(ClickPoint);   
		// Convert from Screen coordinates to Form coordinates    
		Point FormPoint = this.PointToClient(ScreenPoint);   
		// Show context menu   
		contextmenu.MenuItems.Clear();   
		contextmenu.MenuItems.Add('Item1');   
		contextmenu.MenuItems.Add('Item2');   
		contextmenu.Show(this,FormPoint);  
	} 
}

[VB.NET]
Private  Sub treeView1_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs)  
	If e.Button = MouseButtons.Right Then
		Dim ClickPoint As Point =  New Point(e.X,e.Y) 
		Dim ClickNode As TreeNode =  treeView1.GetNodeAt(ClickPoint) 
		If ClickNode Is Nothing Then
			 Return
		End If
		’ Convert from Tree coordinates to Screen coordinates    
		Dim ScreenPoint As Point =  treeView1.PointToScreen(ClickPoint) 
		’ Convert from Screen coordinates to Form coordinates    
		Dim FormPoint As Point =  Me.PointToClient(ScreenPoint) 
		’ Show context menu   
		contextmenu.MenuItems.Clear()   
		contextmenu.MenuItems.Add('Item1')   
		contextmenu.MenuItems.Add('Item2')   
		contextmenu.Show(this,FormPoint)
	End If
End Sub

Permalink

A click event will be fired but a node will not be selected when the user clicks to the right of a node. This code snippets show how you can ensure that a node is selected in this scenario:


[C#]
private void treeView1_Click(object sender, System.EventArgs e)
{
	treeView1.SelectedNode = treeView1.GetNodeAt(treeView1.PointToClient(Cursor.Position));
}
[VB.NET]
Private  Sub treeView1_Click(ByVal sender As Object, ByVal e As System.EventArgs)
	treeView1.SelectedNode = treeView1.GetNodeAt(treeView1.PointToClient(Cursor.Position))
End Sub
Permalink

The following code snippet demonstrates how you can clone or copy all the nodes in TreeView1 to TreeView2 by clicking on Button1.

[C#]
private void  IterateTreeNodes (TreeNode originalNode, TreeNode rootNode)
	{
		foreach( TreeNode childNode in  originalNode.Nodes)
		{
				
			TreeNode newNode = new TreeNode(childNode.Text);		
			newNode.Tag = childNode.Tag;
			this.treeView2.SelectedNode = rootNode;
			this.treeView2.SelectedNode.Nodes.Add(newNode);
			IterateTreeNodes(childNode, newNode);
		}
	}

//Button Click code
private void button1_Click(object sender, System.EventArgs e)
	{
		foreach( TreeNode originalNode in  this.treeView1.Nodes)
		{
			TreeNode newNode = new TreeNode(originalNode.Text);
			newNode.Tag = originalNode.Tag;
			this.treeView2.Nodes.Add(newNode);
			IterateTreeNodes(originalNode, newNode);
		}
	}

[VB.NET]
Private  Sub IterateTreeNodes(ByVal originalNode As TreeNode, ByVal rootNode As TreeNode)
		Dim childNode As TreeNode
		For Each childNode In originalNode.Nodes
 
			Dim NewNode As TreeNode =  New TreeNode(childNode.Text) 
			NewNode.Tag = childNode.Tag
			Me.treeView2.SelectedNode = rootNode
			Me.treeView2.SelectedNode.Nodes.Add(NewNode)
			IterateTreeNodes(childNode, NewNode)
		Next
End Sub
 
’Button Click code
Private  Sub button1_Click(ByVal sender As Object, ByVal e As System.EventArgs)
		Dim originalNode As TreeNode
		For Each originalNode In Me.treeView1.Nodes
			Dim NewNode As TreeNode =  New TreeNode(originalNode.Text) 
			NewNode.Tag = originalNode.Tag
			Me.treeView2.Nodes.Add(NewNode)
			IterateTreeNodes(originalNode, NewNode)
		Next
End Sub

Permalink

Here is a code snippet suggested by Mattias Sjögren on the microsoft.public.dotnet.languages.csharp newsgroup.

[C#]
	public int NodeLevel(TreeNode node)
	{
		int level = 0;
		while ((node = node.Parent) != null) level++;
		return level;
	}
[VB.NET]
	Public Sub NodateLevel(ByVal node as TreeNode) As Integer
		Dim level as Integer = 0
		While Not node Is Nothing
			node = node.Parent
			level = level + 1
		End While
	End Sub
Permalink

Try using a MouseMove event handler and
checking to see if you have moved to a new node, and if so, set a new
tiptext.

[C#]
 	private int oldNodeIndex = -1;
	private ToolTip toolTip1;

	private void Form1_Load(object sender, System.EventArgs e)
	{
		this.toolTip1 = new System.Windows.Forms.ToolTip();
		this.toolTip1.InitialDelay = 300; //half a second delay
		this.toolTip1.ReshowDelay = 0;
	}
	
	private void treeView1_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
	{
		TreeNode tn = this.treeView1.GetNodeAt(e.X, e.Y);
		if(tn != null)
		{
			int currentNodeIndex = tn.Index;
			if(currentNodeIndex != oldNodeIndex)
			{
				oldNodeIndex = currentNodeIndex;
				if(this.toolTip1 != null && this.toolTip1.Active)
					this.toolTip1.Active = false; //turn it off

				this.toolTip1.SetToolTip(this.treeView1, string.Format('tooltip: node {0}', oldNodeIndex));
				this.toolTip1.Active = true; //make it active so it can show
			}
		}
	}

[VB.NET]
	Private oldNodeIndex As Integer = - 1
	Private toolTip1 As ToolTip


	Private Sub Form1_Load(sender As Object, e As System.EventArgs)
   		Me.toolTip1 = New System.Windows.Forms.ToolTip()
   		Me.toolTip1.InitialDelay = 300 ’half a second delay
   		Me.toolTip1.ReshowDelay = 0
	End Sub ’Form1_Load


	Private Sub treeView1_MouseMove(sender As Object, e As System.Windows.Forms.MouseEventArgs)
   		Dim tn As TreeNode = Me.treeView1.GetNodeAt(e.X, e.Y)
   		If Not (tn Is Nothing) Then
      			Dim currentNodeIndex As Integer = tn.Index
      			If currentNodeIndex <> oldNodeIndex Then
         			oldNodeIndex = currentNodeIndex
         			If Not (Me.toolTip1 Is Nothing) And Me.toolTip1.Active Then
            				Me.toolTip1.Active = False ’turn it off
         			End If
         			Me.toolTip1.SetToolTip(Me.treeView1, String.Format('tooltip: node {0}', oldNodeIndex))
         			Me.toolTip1.Active = True ’make it active so it can show
      			End If
   		End If
	End Sub ’treeView1_MouseMove
Permalink

When you drag an item within the TreeView, you can handle the DragOver event for a drag-drop. If you want to drag-drop into a spot that’s not currently visible, you can scroll the TreeView by handling the DragOver event:

[C#]	
	private void treeView1_DragOver(object sender, System.Windows.Forms.DragEventArgs e)
	{
		TreeView tv = sender as TreeView;
		Point pt = tv.PointToClient(new Point(e.X,e.Y));
			
		int delta = tv.Height - pt.Y;
		if ((delta < tv.Height / 2) && (delta > 0))
		{
			TreeNode tn = tv.GetNodeAt(pt.X, pt.Y);
			if (tn.NextVisibleNode != null)
				tn.NextVisibleNode.EnsureVisible();
		}
		if ((delta > tv.Height / 2) && (delta < tv.Height))
		{
			TreeNode tn = tv.GetNodeAt(pt.X, pt.Y);
			if (tn.PrevVisibleNode != null)
				tn.PrevVisibleNode.EnsureVisible();
		}
	} 

[VB.NET]
Private Sub treeView1_DragOver(sender As Object, e As System.Windows.Forms.DragEventArgs)
   
  If TypeOf sender is TreeView  Then
  	Dim tv As TreeView = CType(sender, TreeView)    
  	Dim pt As Point = tv.PointToClient(New Point(e.X, e.Y))
  	Dim delta As Integer = tv.Height - pt.Y
  	If delta < tv.Height / 2 And delta > 0 Then
      		Dim tn As TreeNode = tv.GetNodeAt(pt.X, pt.Y)
      		If Not (tn.NextVisibleNode Is Nothing) Then
         		tn.NextVisibleNode.EnsureVisible()
      		End If 
   	End If 
   	If delta > tv.Height / 2 And delta < tv.Height Then
      		Dim tn As TreeNode = tv.GetNodeAt(pt.X, pt.Y)
      		If Not (tn.PrevVisibleNode Is Nothing) Then
           		tn.PrevVisibleNode.EnsureVisible()
      		End If 
   	End If 
  End If
End Sub ’treeView1_DragOver 
Permalink

Try using the AfterSelect event instead of the Click event. The Click event is inherited from Control.Click and occurs before the new selection is set into SelectedNode. The AfterSelect event is fired after the newly selected node is placed in the SelectedNode property. This code illustrates these events.

	
private void treeView2_AfterSelect(object sender, System.Windows.Forms.TreeViewEventArgs e)
{
	TreeNode node = treeView2.SelectedNode;
	Console.WriteLine('AfterSelect:' + node.ToString());//from tree
	Console.WriteLine('AfterSelect:' + e.Node.ToString());//from event args

}

private void treeView2_Click(object sender, System.EventArgs e)
{
	TreeNode node = treeView2.SelectedNode;
	if(node == null)
		Console.WriteLine('Click: (none)');
	else
		Console.WriteLine('Click: ' + node.ToString());
}
Permalink

Share with

Couldn't find the FAQs you're looking for?

Please submit your question and answer.