|
Excellent job... Possible in VC++
Thanks.
|
|
|
|
|
I am trying to do some thing similar to what you have done, i will study you sample a bit, thanks for sharing your code
|
|
|
|
|
Thanks for the kind works, I hope this proves useful!
Best regards,
Gabe
|
|
|
|
|
Hi.
Good description and nice code made it realy usable and valuable.
However, while analyzing your code I spotted several minor bugs.
1. The drag'n'drop is dependant on the image index of the TreeNode thus preventing us from using different images on different levels (or branches)of the tree.
2. You have used the type string for your NewNodeMap variable, which lead to the frequent memory reallocations, while filling this map. This lead to the unpleasant delays.
3. Many peaces of the code are duplicated about four times (!).
Of course it wouldn't be a comprehansive message if only I wouldn't added my own version of this bugs fixes. I didn't tested it much, but I fixed bugs 2 and 3 and felt myself a bit lazy for the first one
May be my code will inspire you to fix even the first mentioned bug.
private const int MAPSIZE = 128;
private StringBuilder NewNodeMap = new StringBuilder(MAPSIZE);
private void treeView1_DragOver(object sender, System.Windows.Forms.DragEventArgs e)
{
TreeNode NodeOver = this.treeView1.GetNodeAt(this.treeView1.PointToClient(Cursor.Position));
TreeNode NodeMoving = (TreeNode)e.Data.GetData("System.Windows.Forms.TreeNode");
// A bit long, but to summarize, process the following coe only if the nodeover is null
// and either the nodeover is not the same thing as nodemoving UNLESSS nodeover happens
// to be the last node in the branch (so we can allow drag & drop below a parent branch)
if(NodeOver != null && (NodeOver != NodeMoving || (NodeOver.Parent != null && NodeOver.Index == (NodeOver.Parent.Nodes.Count - 1))))
{
int OffsetY = this.treeView1.PointToClient(Cursor.Position).Y - NodeOver.Bounds.Top;
//////////////////////////////////////////////////////////////////////////
//BAD!!!! Can't use images other then with ImageIndex == 1 for the folders
//////////////////////////////////////////////////////////////////////////
// Image index of 1 is the non-folder icon
if(NodeOver.ImageIndex == 1)
{
#region Standard Node
if(OffsetY < (NodeOver.Bounds.Height / 2))
{
//this.lblDebug.Text = "top";
#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
SetNewNodeMap(NodeOver);
if(SetMapsEqual() == true)
return;
#endregion
#region Clear placeholders above and below
this.Refresh();
#endregion
#region Draw the placeholders
DrawLeafTopPlaceholders(NodeOver);
#endregion
}
else
{
//this.lblDebug.Text = "bottom";
#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 Allow drag drop to parent branches
TreeNode ParentDragDrop = null;
// If the node the mouse is over is the last node of the branch we should allow
// the ability to drop the "nodemoving" node BELOW the parent node
if(NodeOver.Parent != null && NodeOver.Index == (NodeOver.Parent.Nodes.Count - 1))
{
int XPos = this.treeView1.PointToClient(Cursor.Position).X;
if(XPos < NodeOver.Bounds.Left)
{
ParentDragDrop = NodeOver.Parent;
while(true)
{
if(XPos > (ParentDragDrop.Bounds.Left -
this.treeView1.ImageList.Images[
ParentDragDrop.ImageIndex].Size.Width))
break;
if(ParentDragDrop.Parent != null)
ParentDragDrop = ParentDragDrop.Parent;
else
break;
}
}
}
#endregion
#region Store the placeholder info into a pipe delimited string
// Since we are in a special case here, use the ParentDragDrop node as the current "nodeover"
TreeNode tnPlaceholderInfo = ParentDragDrop != null ?
ParentDragDrop : NodeOver;
SetNewNodeMap(tnPlaceholderInfo);
if(SetMapsEqual() == true)
return;
#endregion
#region Clear placeholders above and below
this.Refresh();
#endregion
#region Draw the placeholders
DrawLeafBottomPlaceholders(NodeOver, ParentDragDrop);
#endregion
}
#endregion
}
else
{
#region Folder Node
if(OffsetY < (NodeOver.Bounds.Height / 3))
{
//this.lblDebug.Text = "folder top";
#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
SetNewNodeMap(NodeOver);
if(SetMapsEqual() == true)
return;
#endregion
#region Clear placeholders above and below
this.Refresh();
#endregion
#region Draw the placeholders
#endregion
}
else if((NodeOver.Parent != null && NodeOver.Index == 0) &&
(OffsetY > (NodeOver.Bounds.Height - (NodeOver.Bounds.Height / 3))))
{
//this.lblDebug.Text = "folder bottom";
#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
SetNewNodeMap(NodeOver);
if(SetMapsEqual() == true)
return;
#endregion
#region Clear placeholders above and below
this.Refresh();
#endregion
#region Draw the placeholders
DrawFolderTopPlaceholders(NodeOver);
#endregion
}
else
{
//this.lblDebug.Text = "folder over";
if(NodeOver.Nodes.Count > 0)
{
NodeOver.Expand();
//this.Refresh();
}
else
{
#region Prevent the node from being dragged onto itself
if(NodeMoving == NodeOver)
return;
#endregion
#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
SetNewNodeMap(NodeOver);
NewNodeMap = NewNodeMap.Insert(NewNodeMap.Length, "|0");
if(SetMapsEqual() == true)
return;
#endregion
#region Clear placeholders above and below
this.Refresh();
#endregion
#region Draw the "add to folder" placeholder
DrawAddToFolderPlaceholder(NodeOver);
#endregion
}
}
#endregion
}
}
}//eom
private void DrawLeafTopPlaceholders(TreeNode NodeOver)
{
Graphics g = this.treeView1.CreateGraphics();
int NodeOverImageWidth = this.treeView1.ImageList.Images[NodeOver.ImageIndex].Size.Width + 8;
int LeftPos = NodeOver.Bounds.Left - NodeOverImageWidth;
int 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));
}//eom
private void DrawLeafBottomPlaceholders(TreeNode NodeOver, TreeNode ParentDragDrop)
{
Graphics g = this.treeView1.CreateGraphics();
int NodeOverImageWidth = this.treeView1.ImageList.Images[NodeOver.ImageIndex].Size.Width + 8;
// Once again, we are not dragging to node over, draw the placeholder using the ParentDragDrop bounds
int LeftPos, RightPos;
if(ParentDragDrop != null)
LeftPos = ParentDragDrop.Bounds.Left - (this.treeView1.ImageList.Images[ParentDragDrop.ImageIndex].Size.Width + 8);
else
LeftPos = NodeOver.Bounds.Left - NodeOverImageWidth;
RightPos = this.treeView1.Width - 4;
Point[] LeftTriangle = new Point[5]{
new Point(LeftPos, NodeOver.Bounds.Bottom - 4),
new Point(LeftPos, NodeOver.Bounds.Bottom + 4),
new Point(LeftPos + 4, NodeOver.Bounds.Bottom),
new Point(LeftPos + 4, NodeOver.Bounds.Bottom - 1),
new Point(LeftPos, NodeOver.Bounds.Bottom - 5)};
Point[] RightTriangle = new Point[5]{
new Point(RightPos, NodeOver.Bounds.Bottom - 4),
new Point(RightPos, NodeOver.Bounds.Bottom + 4),
new Point(RightPos - 4, NodeOver.Bounds.Bottom),
new Point(RightPos - 4, NodeOver.Bounds.Bottom - 1),
new Point(RightPos, NodeOver.Bounds.Bottom - 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.Bottom), new Point(RightPos, NodeOver.Bounds.Bottom));
}//eom
private void DrawFolderTopPlaceholders(TreeNode NodeOver)
{
Graphics g = this.treeView1.CreateGraphics();
int NodeOverImageWidth = this.treeView1.ImageList.Images[NodeOver.ImageIndex].Size.Width + 8;
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));
}//eom
private void DrawAddToFolderPlaceholder(TreeNode NodeOver)
{
Graphics g = this.treeView1.CreateGraphics();
int RightPos = NodeOver.Bounds.Right + 6;
Point[] RightTriangle = new Point[5]{
new Point(RightPos, NodeOver.Bounds.Y + (NodeOver.Bounds.Height / 2) + 4),
new Point(RightPos, NodeOver.Bounds.Y + (NodeOver.Bounds.Height / 2) + 4),
new Point(RightPos - 4, NodeOver.Bounds.Y + (NodeOver.Bounds.Height / 2)),
new Point(RightPos - 4, NodeOver.Bounds.Y + (NodeOver.Bounds.Height / 2) - 1),
new Point(RightPos, NodeOver.Bounds.Y + (NodeOver.Bounds.Height / 2) - 5)};
this.Refresh();
g.FillPolygon(System.Drawing.Brushes.Black, RightTriangle);
}//eom
private void SetNewNodeMap(TreeNode tnNode)
{
NewNodeMap.Length = 0;
NewNodeMap.Insert(0, (int)tnNode.Index);
TreeNode tnCurNode = tnNode;
while(tnCurNode.Parent != null)
{
tnCurNode = tnCurNode.Parent;
NewNodeMap.Insert(0, tnCurNode.Index + "|");
}
}//oem
private bool SetMapsEqual()
{
if(this.NewNodeMap.ToString() == this.NodeMap)
return true;
else
{
this.NodeMap = this.NewNodeMap.ToString();
return false;
}
}//oem
By the way, thanks for a good job!
|
|
|
|
|
creatio, thanks for your input and your valuable recommended code. I'll make some time to go over it this weekend and implement a new version. I've always known that code reuse was minimal with this project - I didn't have time to fully perfect it.
Thanks for your help!
Gabe
|
|
|
|
|
You wellcome Gabe!
One more very small issue.
It's not safe to rely on ImageIndex property blindly.
As if someone would decide to use DefaultImageIndex and not to assign ImageIndex. The last mentioned will have a value of -1. And your code that uses ImageIndex as an indexer parameter will gladly throw an exception.
int NodeOverImageWidth =
ImageList.Images[NodeOver.ImageIndex].Size.Width + 8; //exception here
A simple check will be enough to overcome this trouble.
int NodeOverImageWidth = (NodeOver.ImageIndex == -1 ?
ImageList.Images[NodeOver.ImageIndex].Size.Width :
ImageList.Images[this.DefaultImageIndex].Size.Width) + 8;
The same problem is in the following code:
// Image index of 1 is the non-folder icon
if(NodeOver.ImageIndex == 1)
However I wouldn't use here the same solution. As any node (theoretically) may become a container, it would be more flexible not to cut the ability to drug items into non folder items. So a timer may be handful in this situation.
The algorithm is: When draged for a specified amount of time - we are getting an "add to folder" state.
If moved - resetting timer, and once again we are in the default positionning state.
This behaviour is like in Explorer (R) tree: dragging over the "+" icon for a second, expands a tree branch.
P.S. My code is a bit different, because I moved all the needed code into the TreeView derived class and compiling it as a custom control DLL.
Best Regards,
Alex.
|
|
|
|
|
Hi,
after reading the comments on this TreeView I wonder why there is an index on the .ImageIndex and not on the .Level .
IMHO when using the level you can be sure that there is no '-1' index value and you allways start with index 0 (zero).
Regards,
Bennie
|
|
|
|
|
Hi Hoopen,
Is .Level a custom property? Could you please give more details?
Regards.
MarcoGaribay
-- modified at 13:16 Wednesday 7th June, 2006
|
|
|
|
|
Intellisense shows that TreeNode .Level "Gets the zero-based depth of the tree node". The help in Visual Studio tells me that it is new in .NET Framework 2.0.
|
|
|
|
|
Hi, great code!!
I am having a little problem getting the target node.
the sourcenode is aquired this way
TreeNode movingNode = (TreeNode)e.Data.GetData("System.Windows.Forms.TreeNode");
but how do i aquire the targetnode???
best regards
|
|
|
|
|
larsiiii,
Sorry for the delay, to acquire the target node you need to navigate the NodeMap. The NodeMap is pipe delimited, so:
2|1|2
Would be:
node0
node1
node2
child0
child1
child0
child1
child2 // HERE!!!!
child2
node3
Best regards,
Gabe
|
|
|
|
|
larsiiii,
Sorry for the delay, to acquire the target node you need to navigate the NodeMap. The NodeMap is pipe delimited, so:
2|1|2
Would be:
node0
node1
node2
+ child0
+ child1
+ + child0
+ + child1
+ + child2 // HERE!!!!
+ child2
node3
Best regards,
Gabe
|
|
|
|
|
Great stuff!!, I tried using you vb code in my visual basic project but it did not like. I would appreciate if you could have put the source and executable files for visual basic too.
|
|
|
|
|
I made an attempt to convert it to VB.NET but ran into a problem. I've updated the article (it should be put online soon). If you view the code you'll see there's a comment that says "Error!". For some reason, at the point of this comment the method completely returns and another event happens. Not sure why.
Best regards,
Gabe
|
|
|
|
|
|
Gabe,
Thanks for the wonderful code. While I feel compelled to agree with some of the other posts regarding code reusability, the example you have provided illustrates how to manipulate the treeview control to reach the desired result, and that’s what makes this code so great. I see some things I will change in my efforts to move this to a user control, however all the elements for the drag and drop functionality have been graciously provided by you.
Thanks again for the code, you’ve jump started my latest application in a HUGE way. Hats off!
-Shawn
|
|
|
|
|
Shawn, this is excellent news. Having generated a few user controls before I realize the benefits, but there are negatives as well. When I finished the code I realized it wasn't a comprehensive solution but it was a heck of a lot better than anything out there in the wild so I rushed to make the code available.
I'm glad you were able to mangle it into your own project. Best of luck with that!
Gabe
|
|
|
|
|
Would it be possible to use that prog in VB.net?
Martin A
|
|
|
|
|
Here is a conversion of this code to VB.Net. It works well but there still may be some bugs in it.
When I get a little more time, I want to compare it line by line with the c# source and make sure there are no errors.
Private Sub TreeViewItemDrag(ByVal sender As Object, ByVal e As System.Windows.Forms.ItemDragEventArgs) Handles TreeView1.ItemDrag
'DoDragDrop(e.Item, DragDropEffects.Move)
' Move the dragged node when the left mouse button is used.
If e.Button = MouseButtons.Left Then
DoDragDrop(e.Item, DragDropEffects.Move)
' Copy the dragged node when the right mouse button is used.
ElseIf e.Button = MouseButtons.Right Then
DoDragDrop(e.Item, DragDropEffects.Copy)
End If
End Sub
Private Sub TreeViewDragEnter(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles TreeView1.DragEnter
e.Effect = DragDropEffects.Move
'e.Effect = e.AllowedEffect
End Sub
Private Sub TreeViewDragDrop(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles TreeView1.DragDrop
If (e.Data.GetDataPresent("System.Windows.Forms.TreeNode", False) AndAlso Not Nodemap = "") Then
Dim MovingNode As TreeNode = e.Data.GetData("System.Windows.Forms.TreeNode")
Dim NodeIndexes() As String = Nodemap.Split("|")
Dim InsertCollection As TreeNodeCollection = TreeView1.Nodes
Dim i As Integer
For i = 0 To NodeIndexes.Length - 2
InsertCollection = InsertCollection(Int32.Parse(NodeIndexes(i))).Nodes
Next
If Not InsertCollection Is Nothing Then
InsertCollection.Insert(Int32.Parse(NodeIndexes(NodeIndexes.Length - 1)), MovingNode.Clone())
TreeView1.SelectedNode = InsertCollection(Int32.Parse(NodeIndexes(NodeIndexes.Length - 1)))
MovingNode.Remove()
End If
End If
End Sub
Private Sub TreeView1_DragOver(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles TreeView1.DragOver
Dim NodeOver As TreeNode = Me.TreeView1.GetNodeAt(Me.TreeView1.PointToClient(System.Windows.Forms.Cursor.Position))
Dim NodeMoving As TreeNode = e.Data.GetData("System.Windows.Forms.TreeNode")
'A bit long, but to summarize, process the following coe only if the nodeover is null
'and either the nodeover is not the same thing as nodemoving UNLESSS nodeover happens
'to be the last node in the branch (so we can allow drag & drop below a parent branch)
' true true false
If (Not NodeOver Is Nothing AndAlso (Not NodeOver Is NodeMoving OrElse (Not NodeOver.Parent Is Nothing AndAlso (NodeOver.Index = NodeOver.Parent.Nodes.Count - 1)))) Then
Dim OffsetY As Int32 = Me.TreeView1.PointToClient(System.Windows.Forms.Cursor.Position).Y - NodeOver.Bounds.Top
Dim NodeOverImageWidth As Integer = Me.TreeView1.ImageList.Images(NodeOver.ImageIndex).Size.Width + 8
Dim g As Graphics = Me.TreeView1.CreateGraphics()
'Image index of 1 is the non-folder icon
If NodeOver.ImageIndex = 1 Then
'Standard Node
If (OffsetY < (NodeOver.Bounds.Height / 2)) Then
'this.lblDebug.Text = "top"
'If NodeOver is a child then cancel
Dim tnParadox As TreeNode = NodeOver
While Not tnParadox.Parent Is Nothing
If tnParadox.Parent Is NodeMoving Then
NodeMap = ""
Return
End If
tnParadox = tnParadox.Parent
End While
'Store the placeholder info into a pipe delimited string
Dim tnPlaceholderInfo As TreeNode = NodeOver
Dim NewNodeMap As String = NodeOver.Index.ToString()
While Not tnPlaceholderInfo.Parent Is Nothing
tnPlaceholderInfo = tnPlaceholderInfo.Parent
NewNodeMap = tnPlaceholderInfo.Index.ToString + "|" + NewNodeMap
End While
If NewNodeMap Is NodeMap Then
Return
Else
NodeMap = NewNodeMap
End If
'Clear placeholders above and below
Me.Refresh()
'Draw the placeholders
Dim LeftPos As Int32, RightPos As Int32
LeftPos = NodeOver.Bounds.Left - NodeOverImageWidth
RightPos = TreeView1.Width - 4
Dim LeftTriangle() As Point = { _
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)}
Dim RightTriangle() As Point = { _
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))
Else
'lblDebug.Text = "bottom"
'If NodeOver is a child then cancel
Dim tnParadox As TreeNode = NodeOver
While Not tnParadox.Parent Is Nothing
If tnParadox.Parent Is NodeMoving Then
NodeMap = ""
Return
End If
tnParadox = tnParadox.Parent
End While
'Allow drag drop to parent branches
Dim ParentDragDrop As TreeNode = Nothing
'If the node the mouse is over is the last node of the branch we should allow
'the ability to drop the "nodemoving" node BELOW the parent node
If Not NodeOver.Parent Is Nothing AndAlso NodeOver.Index = NodeOver.Parent.Nodes.Count - 1 Then
Dim XPos As Integer = Me.TreeView1.PointToClient(System.Windows.Forms.Cursor.Position).X
If (XPos < NodeOver.Bounds.Left) Then
ParentDragDrop = NodeOver.Parent
While (True)
If (XPos > (ParentDragDrop.Bounds.Left - TreeView1.ImageList.Images(ParentDragDrop.ImageIndex).Size.Width)) Then
Exit Sub 'break()
End If
If Not ParentDragDrop.Parent Is Nothing Then
ParentDragDrop = ParentDragDrop.Parent
Else
Exit Sub 'break()
End If
End While
End If
End If
'Store the placeholder info into a pipe delimited string
'Since we are in a special case here, use the ParentDragDrop node as the current "nodeover"
Dim tnPlaceholderInfo As TreeNode
If Not ParentDragDrop Is Nothing Then
tnPlaceholderInfo = ParentDragDrop
Else
tnPlaceholderInfo = NodeOver
End If
Dim NewNodeMap As String = (tnPlaceholderInfo.Index + 1).ToString()
While Not tnPlaceholderInfo.Parent Is Nothing
tnPlaceholderInfo = tnPlaceholderInfo.Parent
NewNodeMap = tnPlaceholderInfo.Index.ToString + "|" + NewNodeMap
End While
If NewNodeMap = NodeMap Then
Return
Else
NodeMap = NewNodeMap
End If
'Clear placeholders above and below
Me.Refresh()
'Draw the placeholders
'Once again, we are not dragging to node over, draw the placeholder using the ParentDragDrop bounds
Dim LeftPos As Int32, RightPos As Int32
If Not ParentDragDrop Is Nothing Then
LeftPos = ParentDragDrop.Bounds.Left - (TreeView1.ImageList.Images(ParentDragDrop.ImageIndex).Size.Width + 8)
Else
LeftPos = NodeOver.Bounds.Left - NodeOverImageWidth
End If
RightPos = Me.TreeView1.Width - 4
Dim LeftTriangle() As Point = { _
New Point(LeftPos, NodeOver.Bounds.Bottom - 4), _
New Point(LeftPos, NodeOver.Bounds.Bottom + 4), _
New Point(LeftPos + 4, NodeOver.Bounds.Bottom), _
New Point(LeftPos + 4, NodeOver.Bounds.Bottom - 1), _
New Point(LeftPos, NodeOver.Bounds.Bottom - 5)}
Dim RightTriangle() As Point = { _
New Point(RightPos, NodeOver.Bounds.Bottom - 4), _
New Point(RightPos, NodeOver.Bounds.Bottom + 4), _
New Point(RightPos - 4, NodeOver.Bounds.Bottom), _
New Point(RightPos - 4, NodeOver.Bounds.Bottom - 1), _
New Point(RightPos, NodeOver.Bounds.Bottom - 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.Bottom), New Point(RightPos, NodeOver.Bounds.Bottom))
End If
Else 'must be a folder icon
'Folder Node
If (OffsetY < (NodeOver.Bounds.Height / 3)) Then
'this.lblDebug.Text = "folder top"
'If NodeOver is a child then cancel
Dim tnParadox As TreeNode = NodeOver
While Not tnParadox.Parent Is Nothing
If tnParadox.Parent Is NodeMoving Then
NodeMap = ""
Return
End If
tnParadox = tnParadox.Parent
End While
'Store the placeholder info into a pipe delimited string
Dim tnPlaceholderInfo As TreeNode = NodeOver
Dim NewNodeMap As String = NodeOver.Index.ToString()
While Not tnPlaceholderInfo.Parent Is Nothing
tnPlaceholderInfo = tnPlaceholderInfo.Parent
NewNodeMap = tnPlaceholderInfo.Index.ToString + "|" + NewNodeMap
End While
If NewNodeMap = NodeMap Then
Return
Else
NodeMap = NewNodeMap
End If
'Clear placeholders above and below
Me.Refresh()
'Draw the placeholders
Dim LeftPos As Int32, RightPos As Int32
LeftPos = NodeOver.Bounds.Left - NodeOverImageWidth
RightPos = TreeView1.Width - 4
Dim LeftTriangle() As Point = { _
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)}
Dim RightTriangle() As Point = { _
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))
ElseIf Not NodeOver.Parent Is Nothing AndAlso NodeOver.Index = 0 AndAlso (OffsetY > (NodeOver.Bounds.Height - (NodeOver.Bounds.Height / 3))) Then
'Me.lblDebug.Text = "folder bottom"
'If NodeOver is a child then cancel
Dim tnParadox As TreeNode = NodeOver
While Not tnParadox.Parent Is Nothing
If tnParadox.Parent Is NodeMoving Then
Nodemap = ""
Return
End If
tnParadox = tnParadox.Parent
End While
'Store the placeholder info into a pipe delimited string
Dim tnPlaceholderInfo As TreeNode = NodeOver
Dim NewNodeMap As String = NodeOver.Index + 1.ToString()
While Not tnPlaceholderInfo.Parent Is Nothing
tnPlaceholderInfo = tnPlaceholderInfo.Parent
NewNodeMap = tnPlaceholderInfo.Index.ToString + "|" + NewNodeMap
End While
If NewNodeMap = Nodemap Then
Return
Else
Nodemap = NewNodeMap
End If
'Clear placeholders above and below
Me.Refresh()
'Draw the placeholders
Dim LeftPos As Int32, RightPos As Int32
LeftPos = NodeOver.Bounds.Left - NodeOverImageWidth
RightPos = Me.TreeView1.Width - 4
Dim LeftTriangle() As Point = { _
New Point(LeftPos, NodeOver.Bounds.Bottom - 4), _
New Point(LeftPos, NodeOver.Bounds.Bottom + 4), _
New Point(LeftPos + 4, NodeOver.Bounds.Bottom), _
New Point(LeftPos + 4, NodeOver.Bounds.Bottom - 1), _
New Point(LeftPos, NodeOver.Bounds.Bottom - 5)}
Dim RightTriangle() As Point = { _
New Point(RightPos, NodeOver.Bounds.Bottom - 4), _
New Point(RightPos, NodeOver.Bounds.Bottom + 4), _
New Point(RightPos - 4, NodeOver.Bounds.Bottom), _
New Point(RightPos - 4, NodeOver.Bounds.Bottom - 1), _
New Point(RightPos, NodeOver.Bounds.Bottom - 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.Bottom), New Point(RightPos, NodeOver.Bounds.Bottom))
Else
'folder over
If (NodeOver.Nodes.Count > 0) Then
NodeOver.Expand()
'Me.Refresh()
Else
'prevent node from being dragged onto itself
If NodeMoving Is NodeOver Then Return
'If NodeOver is a child then cancel
Dim tnParadox As TreeNode = NodeOver
While Not tnParadox.Parent Is Nothing
If tnParadox.Parent Is NodeMoving Then
NodeMap = ""
Return
End If
tnParadox = tnParadox.Parent
End While
'Store the placeholder info into a pipe delimited string
Dim tnPlaceholderInfo As TreeNode = NodeOver
Dim NewNodeMap As String = NodeOver.Index.ToString()
While Not tnPlaceholderInfo.Parent Is Nothing
tnPlaceholderInfo = tnPlaceholderInfo.Parent
NewNodeMap = tnPlaceholderInfo.Index.ToString + "|" + NewNodeMap
End While
NewNodeMap = NewNodeMap + "|0"
If (NewNodeMap = NodeMap) Then
Return
Else
NodeMap = NewNodeMap
End If
'Clear placeholders above and below
Me.Refresh()
' Draw the "add to folder" placeholder
Dim RightPos As Int16 = NodeOver.Bounds.Right + 6
Dim RightTriangle() As Point = {New Point(RightPos, NodeOver.Bounds.Y + (NodeOver.Bounds.Height / 2) + 4), New Point(RightPos, NodeOver.Bounds.Y + (NodeOver.Bounds.Height / 2) + 4), New Point(RightPos - 4, NodeOver.Bounds.Y + (NodeOver.Bounds.Height / 2)), New Point(RightPos - 4, NodeOver.Bounds.Y + (NodeOver.Bounds.Height / 2) - 1), New Point(RightPos, NodeOver.Bounds.Y + (NodeOver.Bounds.Height / 2) - 5)}
Me.Refresh()
g.FillPolygon(System.Drawing.Brushes.Black, RightTriangle)
End If 'folder over
End If 'folder top, bottom, over
End If 'node or folder
End If '1st if
End Sub
|
|
|
|
|
I haven't done a word for word check of the VB code yet, but I have found this error:
There is a line that says...
If NewNodeMap Is NodeMap
It should say...
If NewNodeMap = NodeMap
This is the only bug in the VB code I have found so far.
|
|
|
|
|
Clark, thanks for the conversion. I'll look it over and try to implement a VB conversion with your code along with other recommendations I've gotten.
Thanks for your help!
Gabe
|
|
|
|
|
A nice article, and with nice explanation of the concepts and problems involved.
About code reuse, your approach is reusable: it's not reasonable to think that only because you created a custom control, it would be reusable. I have already derived my own tree control and I would need to copy & paste or recoding it, anyways.
I really like your approach to writing articles: you made it really simple to understand.
Got my 5!
Yes, even I am blogging now!
|
|
|
|
|
While everybody is entitled to their opinion (see Not Reusable)
Rather than wast a lot of time bitching use this article as a base and create a reusable example!!
ie Put up or give it up!!
As for my opinion it is a great example that solves a really tough problem!!
Ironic how easy it is compared to MFC
Thanx!!
|
|
|
|
|
You're very welcome and thanks for the words of support.
|
|
|
|
|
1. Do you have any ideas how to create custom cursor while draging?
2. How to stop draging if user presses right mouse button
It seems like GiveFeedback and QueryContinueDrag events are not working with treeview...
jerry
|
|
|
|
|