What is the most accurate way to move a node up and down in a treeview. I got a context menu on each node and the开发者_如何学运维 selected node should be moved with all its subnodes.
I'm using C# .Net 3.5 WinForms
You can use the following extensions :
public static class Extensions
{
public static void MoveUp(this TreeNode node)
{
TreeNode parent = node.Parent;
TreeView view = node.TreeView;
if (parent != null)
{
int index = parent.Nodes.IndexOf(node);
if (index > 0)
{
parent.Nodes.RemoveAt(index);
parent.Nodes.Insert(index - 1, node);
}
}
else if (node.TreeView.Nodes.Contains(node)) //root node
{
int index = view.Nodes.IndexOf(node);
if (index > 0)
{
view.Nodes.RemoveAt(index);
view.Nodes.Insert(index - 1, node);
}
}
}
public static void MoveDown(this TreeNode node)
{
TreeNode parent = node.Parent;
TreeView view = node.TreeView;
if (parent != null)
{
int index = parent.Nodes.IndexOf(node);
if (index < parent.Nodes.Count -1)
{
parent.Nodes.RemoveAt(index);
parent.Nodes.Insert(index + 1, node);
}
}
else if (view != null && view.Nodes.Contains(node)) //root node
{
int index = view.Nodes.IndexOf(node);
if (index < view.Nodes.Count - 1)
{
view.Nodes.RemoveAt(index);
view.Nodes.Insert(index + 1, node);
}
}
}
}
Child nodes will follow their parents.
EDIT: Added case that node to move is a root in the TreeView.
While I feel writing this code is a waste of time, given the lack of response to comments by the OP, the least I can do is show how the code example by Le-Savard can be fixed so that muliple clicks of the up or down choice on the context menu ... assuming the context menu is not auto-closed each time and the user is forced to select the same node over and over again ... will do the right thing with the orignally selected node, and not create un-intended side effects :
public static class Extensions
{
public static void MoveUp(this TreeNode node)
{
TreeNode parent = node.Parent;
if (parent != null)
{
int index = parent.Nodes.IndexOf(node);
if (index > 0)
{
parent.Nodes.RemoveAt(index);
parent.Nodes.Insert(index - 1, node);
// bw : add this line to restore the originally selected node as selected
node.TreeView.SelectedNode = node;
}
}
}
public static void MoveDown(this TreeNode node)
{
TreeNode parent = node.Parent;
if (parent != null)
{
int index = parent.Nodes.IndexOf(node);
if (index < parent.Nodes.Count - 1)
{
parent.Nodes.RemoveAt(index);
parent.Nodes.Insert(index + 1, node);
// bw : add this line to restore the originally selected node as selected
node.TreeView.SelectedNode = node;
}
}
}
}
Of course this fix, still does not address the fact that in the example code that multiple root nodes cannot be moved (since they are 'parentless) : that's easiliy fixable.
Nor does it address the more interesting case where moving up a top child-node means you make some interpretation of where that "promoted" child code should go : exactly the same "strategic choice" is involved where you "move down" the last child node of a parent node and are thus required to decide where it should go. In Dynami Le-Savard's code : these cases are just ignored.
However, it is a design-choice to restrict child node from only being moved within their parent nodes Nodes collection : a design choice that may be perfectly suitable for one solution.
Similarly, it is a design choice to force a user to select a node and context-click to get a context menu that allows a choice of moving up or down every single time they want to move it : that's not a design choice I'd make : I'd be using drag-and-drop here or buttons that allow repeated rapid-fire relocation of any selected node anywhere in the tree.
By the way I like Dynami Le-Savard's use of extensions here.
Here's a solution that allows you to drag & drop nodes to wherever you want. To move a node to the same level as another node, just hold down shift when dropping the node. This is a really easy way to go compared to the alternatives and their potential problems. Example was written with a more recent version of .Net (4.5).
Note: Be sure and AllowDrop=true on the treeview control otherwise you can't drop nodes.
/// <summary>
/// Handle user dragging nodes in treeview
/// </summary>
private void treeView1_ItemDrag(object sender, ItemDragEventArgs e)
{
DoDragDrop(e.Item, DragDropEffects.Move);
}
/// <summary>
/// Handle user dragging node into another node
/// </summary>
private void treeView1_DragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Move;
}
/// <summary>
/// Handle user dropping a dragged node onto another node
/// </summary>
private void treeView1_DragDrop(object sender, DragEventArgs e)
{
// Retrieve the client coordinates of the drop location.
Point targetPoint = treeView1.PointToClient(new Point(e.X, e.Y));
// Retrieve the node that was dragged.
TreeNode draggedNode = e.Data.GetData(typeof(TreeNode));
// Sanity check
if (draggedNode == null)
{
return;
}
// Retrieve the node at the drop location.
TreeNode targetNode = treeView1.GetNodeAt(targetPoint);
// Did the user drop the node
if (targetNode == null)
{
draggedNode.Remove();
treeView1.Nodes.Add(draggedNode);
draggedNode.Expand();
}
else
{
TreeNode parentNode = targetNode;
// Confirm that the node at the drop location is not
// the dragged node and that target node isn't null
// (for example if you drag outside the control)
if (!draggedNode.Equals(targetNode) && targetNode != null)
{
bool canDrop = true;
while (canDrop && (parentNode != null))
{
canDrop = !Object.ReferenceEquals(draggedNode, parentNode);
parentNode = parentNode.Parent;
}
if (canDrop)
{
// Have to remove nodes before you can move them.
draggedNode.Remove();
// Is the user holding down shift?
if (e.KeyState == 4)
{
// Is the targets parent node null?
if (targetNode.Parent == null)
{
// The target node has no parent. That means
// the target node is at the root level. We'll
// insert the node at the root level below the
// target node.
treeView1.Nodes.Insert(targetNode.Index + 1, draggedNode);
}
else
{
// The target node has a valid parent so we'll
// drop the node into it's index.
targetNode.Parent.Nodes.Insert(targetNode.Index + 1, draggedNode);
}
}
else
{
targetNode.Nodes.Add(draggedNode);
}
targetNode.Expand();
}
}
}
// Optional: The following lines are an example of how you might
// provide a better experience by highlighting and displaying the
// content of the dropped node.
// treeView1.SelectedNode = draggedNode;
// NavigateToNodeContent(draggedNode.Tag);
}
精彩评论