|
that is too personal.
bang ur head into the wall.
look at the main topic of this article, he din post a "let refactoring treeview code etc"
every article has its objective.
from,
-= aLbert =-
|
|
|
|
|
No, actually, I agree with zavitax (four years after the fact, no less). This code was (and still is) actually pretty terrible. Let's list the points:
(1) Using #region as a replacement for single-line comments
- Just use //! Allows other programmers to see both the intent of the code and the code itself. No need to "hide" anything under #region.
(2) Absurd amount of copy-pasting
- The DragOver event handler has code copy-pasted numerous times. Why was this not generalized?
(3) Using "image index" to drive
- This is actually my biggest frustration. I do not use the same image indices, nor would I even want to tie functionality/meaning into them. TreeNode image indices are a visual tool only (imho). In order to use this code I have to figure out a way to notify the parent control that I'm about to move a node, and where, and whether or not it should be allowed. Or figure out a smarter way of adding permissions/flags to nodes.
So - I suppose it's a nice proof of concept, but it's not as usable I had hoped it to be.
And yes, I understand the point of the code was to illustrate how to do something. But what you have to understand is that when you post code online as an [i]example[/i] to others it serves as that: an example. Not just in functionality, but also in style and "good practice." A junior programmer may look at this and think: "Hey, this ain't so bad. He did it, lots of people like the code, must be okay."
So please, when building sample code, be an example of good coding practices as well
modified on Monday, October 12, 2009 8:56 PM
modified 2-Nov-21 21:01pm.
|
|
|
|
|
Loser. i know your mouth just want to say something.
Do it and SHOW IT to the community.
if this thread is talking about Design Pattern or Refactoring, then i agree with you. again, u will never convince me. i respect programmer who do the job, not just talker.
and i see, you never even submit an article. welldone man.
from,
-= aLbert =-
|
|
|
|
|
I changed it to work off of tags instead of image indicies really quickly... it's not that hard.
|
|
|
|
|
By only slightly modifying the code I was able to drag nodes between multiple treeviews. This has saved me a LOT of headaches, awesome work!!
|
|
|
|
|
Hey Chals, I'm glad the code was helpful
Gabe
|
|
|
|
|
Hi :
How can I darg-drop item to a WebBrowser control ?
I created an application written by C#,it includes two forms , one form
includes a TreeView control , the other includes a WebBrower control and a
TextBox control, now we call them as Form1 and Form2.
First , I try drag a item to the TextBox control,(I have set TreeView for
drag well ) I set
"this.textBox1.AllowDrop = true;" then add Events and write these methods:
this.textBox1.DragOver += new System.Windows.Forms.DragEventHandler
(this.textBox1_DragOver);
this.textBox1.DragDrop += new System.Windows.Forms.DragEventHandler
(this.textBox1_DragDrop);
this.textBox1.DragEnter += new System.Windows.Forms.DragEventHandler
(this.textBox1_DragEnter);
Run this application,and drag item from TreeView which in Form1 to TextBox
which in Form2,the TextBox 3 Events (DragOver,DragDrop,DragEnter) is done
well.
But when I want drag item to the WebBrower,I can not find DragEnter Event and
others Events related to "Drag-Drop",so I can not drop Item to WebBrowser.
Also I have tryed set WebBrowser's Document.DesignMode="On",but can not drop
too.
Then I Write a HTMLBody's ondragenter Event ,but this ondragenter() can not
trigger.
So I think it must be solved by COM knowledge.
Can any one give my some suggestion,
because my COM knowledge is poor, if anyone give me an all solved code ,I
will be very appreciated.Thanks very
|
|
|
|
|
Hey,
I'm sorry, I have only a little bit of experience with the web browser control and one thing I remember is that it is not fully supported by .NET and buggy.
Sorry I couldn't be more help.
Gabe
|
|
|
|
|
Possible conversion in VC++?
|
|
|
|
|
Anything is possible in VC++ however I do not have the time to convert this project over to that language - sorry.
|
|
|
|
|
Thanks for your prompt reply..
|
|
|
|
|
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
|
|
|
|
|