i need to clone an entire treeview to implement Undo\Redo actions. i try in different ways but it appears that directly isn't possible. I mean copying the entire treeview, save that copy in a list. Then when the Undo action is requested decrement a specific counter of levels of undo and replace the actual treeview with the treeview that exist in the index positions of the list. if I do redo the same happens but lvls is incremented so i recover wh开发者_JS百科at come next. Every time that a modifications occurs in the treeview i need to save that copy and increment the lvl counter
I think you should consider going about this a different way.
You don't need to store the entire TreeView in order to keep track of undos and redos. You only need to keep track of the changes:
- If you add a node, the undo is to remove it, so you only need a reference to that node.
- If you remove it, the undo is to add it, so you need a reference to the added node and the node it was added to.
- If you delete a whole branch, then you need to store the branch and the node it was deleted from.
And so forth. I think you'll have better luck doing it this way than trying to store the entire tree. And I've done it this way in applications where I've implemented undo/redo.
Instead of cloning the treeview itself you could simply clone the TreeNodes (see TreeNode.Clone()). It seems like the important bit is the relationship between the nodes, so this would probably work for you (I say "probably" because I haven't actually done this myself, so I can't tell you if there are any caveats to be wary of).
Consider implementing Command pattern (GoF):
- Put your actions logic into classes which implement common ICommand {Do(); Undo();} interface.
- On each user action you create object of command requested and initialize it with context parameters like new and old filename.
- Call Do(), put object into stack of completed commands.
- Each command is supplied with context, so by calling Undo() it can reverse changes.
- Consider moving files into temporary folder instead of removals.
Also, for a quick linear undo/redo, another option is the Memento pattern using zip of file as memento.
This is my shot at it, no big objects being moved in memory:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.Drawing;
namespace treeundoredo
{
static class Program
{
static TreeNode GetNode(string id, TreeNodeCollection root)
{
foreach (TreeNode node in root)
if (node.Text == id)
return node;
else
{
var subnode = GetNode(id, node.Nodes);
if (subnode != null)
return subnode;
}
return null;
}
[STAThread]
static void Main()
{
// list of (node, parent)
List<KeyValuePair<string, string>> undo = new List<KeyValuePair<string, string>>(), redo = new List<KeyValuePair<string, string>>();
Random rng = new Random();
TreeView Tree = new TreeView() { Size = new Size(200, 250) };
Tree.Nodes.Add("Root");
Tree.NodeMouseClick += (s, e) => { undo.Add(new KeyValuePair<string, string>(e.Node.Nodes.Add(rng.Next().ToString()).Text, e.Node.Text)); Tree.ExpandAll(); redo.Clear(); };
Button Undo = new Button() { Text = "Undo", Left = 205 };
Undo.Click += (s, e) => { if (undo.Count > 0) { var kvp = undo[undo.Count - 1]; GetNode(kvp.Key, Tree.Nodes).Remove(); redo.Add(kvp); undo.Remove(kvp); } };
Button Redo = new Button() { Text = "Redo", Left = 205, Top = 50 };
Redo.Click += (s, e) => { if (redo.Count > 0) { var kvp = redo[redo.Count - 1]; GetNode(kvp.Value, Tree.Nodes).Nodes.Add(kvp.Key); redo.Remove(kvp); undo.Add(kvp); Tree.ExpandAll(); } };
Form frm = new Form();
frm.Controls.AddRange(new Control[] { Tree, Undo, Redo });
Application.Run(frm);
}
}
}
精彩评论