|
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
|
|
|
|
|
Thanks, sorry the .exe was not there. I must have forgot to recompile since my last update.
Best regards,
Gabe
|
|
|
|
|
Hi,
I can appreciate your effort but...
This is really a Microsoft style programming:
ABSOLUTLY NOT REUSABLE!
How many people put all the code inside forms!
Why don't you write a customized control?
Sorry, Really BAD!
Razor
|
|
|
|
|
Not everything here on Code Project is a ready-to-use control you can drop on a form, nor should it be. This articlde is a tutorial on how to accomplish something quite useful with a TreeView control, and judging by the other comments, it was useful to many people.
Your comments are quite unconstructive and certainly not appreciated.
Charlie
if(!curlies){ return; }
|
|
|
|
|
This should be a place where sharing good code...
Please give me 1 good reason for which all the code shouldn't be in a custom control!
I have an answer: You just wrote 1 application in your life with 1 only tree control...
and you continue to make copy and paste all the time in all forms in which you use a treeview control.
I remember a lesson from N. Wirth: "Before doing Copy and Paste think 2 times.. you are going to have something not reusable/maintenable"
Maybe your comment is not contructive as you just answer to me without giving 1 reason for which this code should be in a Form!
I'm constructive as I ask again to the author to put this code in a control giving something reusable.
It's just a question to make a small effort!
Razor
|
|
|
|
|
If this article were a tutorial on building user controls, you would have a very valid point.
You are being critical of this article not because of the code it contains, but because of the fact that it not a ready-for-primetime user control that you can use in your projects. That is an unfair judgement when you consider the fact that that is not what the author intended in the first place.
It is what it was intended to be: a tutorial. Not one on building a user control, but one on coding against a TreeView .
Charlie
if(!curlies){ return; }
|
|
|
|
|
As it seems you don't understand, I will give some reason for understanding the story of copy paste, and why this kind of coding cannot be considered a Tutorial:
1. This code is repeated 5 times in the same method!!! This guy use Region as some kind of
functions?
TreeNode tnParadox = NodeOver;
while(tnParadox.Parent != null)
{
if(tnParadox.Parent == NodeMoving)
{
this.NodeMap = "";
return;
}
tnParadox = tnParadox.Parent;
}
2. This code is 3 times
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));
3. What is for the else?
if(NewNodeMap == this.NodeMap)
return;
else
this.NodeMap = NewNodeMap;
I will tell you again and again: This is not a good programming example!!!
There are dozen of useless line codes!
That's really bad!
Razor.
|
|
|
|
|
All that is fine and good, but the reasons you just stated as to why you don't think this is a good example have nothing to do with your orginal argument. You contended in your original post that the code was bad simply because it was not encaplsulated in a control. That is what I took exception to. And that is certainly different from your latest post.
Charlie
if(!curlies){ return; }
|
|
|
|
|
Razorina,
As it seems you don't understand that the codeproject is based on contributing code and not thread crap bitch and moans about how un-portable the code is.
1) You can go buy a ready made control from one of the many control vendors.
2) You can attempt to contribute by fixing what you deem to be mistakes.
3) You can continue crying about how bad this code is and why its hard for your lazy ass to port it.
Grow up.
Gabe
|
|
|
|
|
Gabe,
1. I'm not sure that the coding in commercial software is always good, as I'm sure that on this site you can find very good examples for free, of course your is not one of that.
2. It's impossible to fix your code. Please rewrite it!
3. I just answer to Charlie, that finally seems to agree that you should review your code!
4. Where did you learn to write such a messy code?
5. Stop doing copy and paste and try to reuse!
Razorino
|
|
|
|
|
that is too personal.
bang ur head into the wall.
look at the main topic of this article, he din post a "lets refactoring treeview code etc"
every article has its objective.
from,
-= aLbert =-
|
|
|
|
|
Wow.
After reading through this thread, I've come to understand two things about our dear friend Razorino:
- His grasp of English is exceedingly poor, and he lacks the ability to properly proofread his posts.
- He seems to lack the neccessary knowledge required to take the spirit of the proposed solution and write his very own reusable control.
- He seems to feel it is the authors duty to provide him with a commercial quality control, for free, that he can simply use in his own projects.
Ah, yes, a code troll. Hmm... might I have coined a new name for a most common classification of programmer?
|
|
|
|
|
Hi agree with the last post,
but Razoniro have said something right about the reusability and, in futur, the maintenance of the source code. I think that if you have something to do, do it right first time.
Personaly, after spending a lot of time searching on the Net, this Drag'n Drop treeview example give the best features I can find, but... Jesus Christ ! For some reasons, it's very difficult to change / adapting the code to rapidly making a custom, ultimate TreeView control that is reusable through my multiple forms and applications. About this point, I'm very disappointed...
Finally, it's look that we forget to said thank you at Microsoft for his crappy control named TreeView that don't support multi-select, drag'n drop and other kind of features like these...
but after all I'll not forget to respectfully said thank you to Gabe
|
|
|
|
|
how about multi item dragging?
|
|
|
|
|
Sounds fairly straight forward. I would think the toughest item would be the GDI work to create a bounding box.
|
|
|
|
|
After enabling the Enable drag drop rearrange support, I added few nodes and started to click many times on one. Then when I drugged the node, it showed up as few new the same nodes in the new location. It seems like the multiple clicks on the node before dragging may cause “cloning” of the nodes…Does the same happen to you?
|
|
|
|
|
I am sorry. If I click on the button “Enable drag drop rearrange support” more then once then this problem appears. Basically I subscribed to all drag and drop events multiple times. I would just disable that button after first click in the demo application.
|
|
|
|
|
Good request, I never fully tested the demo'ing of the code but I'll add in this logic asap.
Best regards,
Gabe
|
|
|
|
|
Hi,
I tried your code and it seems to have a problem. When you add 2 folders and take the second folder and drag on the first one, then second folder becomes the sub-folder of the first folder. Then when you try and drag the second folder over itself now, it goes missing. I am not sure if its designed to do so. . Also, Could the adding of folders and nodes be from the point where the current selection is, rather than adding it in the end?
Otherwise, its a great piece of code!
Cheers!
Vijay Kanth
Wipro Technologies.
|
|
|
|
|