Der folgende TreeView kann seine Hierarchie aufgrund des FullPaths flach speichern und wieder genauso aufbauen.
using System;
using System.Collections;
using System.Windows.Forms;
namespace TreeRestore
{
/// <summary>
/// Basierend auf der Idee einen hierarchischen TreeView aufgrund des FullPaths der einzelnen
/// TreeNodes flach zu speichern entstand dieses kurze Sample.
///
/// Idee und Realisierung: Programmierhans
/// </summary>
public class TreeRestore : TreeView
{
//speichert während des Ladens die bereits erstellten Nodes
//Key= FullPath (mit PathSeparator am Ende)
//Value= Node
private Hashtable _HtNodes=new Hashtable();
/// <summary>
/// Constructor
/// </summary>
public TreeRestore()
{
}
/// <summary>
/// Lädt die Daten wieder in den Tree
/// </summary>
/// <param name="pStrings">die ArrayList aus SaveToStringArrayList</param>
public void LoadFromStringArrayList(ArrayList pStrings)
{
//Lösche alle Nodes
this.Nodes.Clear();
//Lösche den Inhalt der Hashtable
this._HtNodes.Clear();
//Sortiere die Strings
pStrings.Sort();
foreach (string s in pStrings)
{
//Erstelle die Nodes auf Root-Ebene startend rekursiv
this.CreateNodeOnNodes(this.Nodes,s,string.Empty);
}
}
private void CreateNodeOnNodes(TreeNodeCollection pNodes, string pString, string pParentPath)
{
//pString = A1\B1\C1
//die Hierarchie dieses Nodes (Node C1 hängt an B1 welcher an A1 hängt)
//z.B:
//[0]="A1"
//[1]="B1\C1"
string[] hierarchy=pString.Split(this.PathSeparator.ToCharArray(),2);
//key ist der Pfad eines allenfalls schon erstellten Nodes (incl. PathSeparator am Ende)
//also z.B: A1\
string relativeKey=string.Concat(hierarchy[0],this.PathSeparator);
string restString = pString.Replace(relativeKey,string.Empty);
string key=string.Concat(pParentPath, relativeKey);
if (restString!=string.Empty)
{
//wenn der Node A1 (Key = A1\ schon existiert muss an diesem angehängt werden)
if (this._HtNodes.ContainsKey(key))
{
System.Diagnostics.Debug.WriteLine(string.Format("ParamString: {0} \tParentPath: {1} \tNode gefunden: {2} \trekursiver call mit RestString {3}",pString,pParentPath,key,restString));
pNodes=(TreeNodeCollection)this._HtNodes[key];
this.CreateNodeOnNodes(pNodes,restString,key);
}
else
{
System.Diagnostics.Debug.WriteLine(string.Format("ParamString: {0} \tParentPath: {1} \tErstelle Node: {2} \tauf Parent: {3} \tKey: {4}",pString,pParentPath,restString,pParentPath,key));
//der Node A1 existiert noch nicht also erstelle diesen
TreeNode nd=new TreeNode(restString);
pNodes.Add(nd);
//als Key A1\ adden
this._HtNodes.Add(key,nd.Nodes);
}
}
}
public ArrayList SaveToStringArrayList()
{
ArrayList arrRet=new ArrayList();
this.AddNodes(this.Nodes,arrRet);
return arrRet;
}
private void AddNodes(TreeNodeCollection pNodes, ArrayList pStrings)
{
foreach (TreeNode nd in pNodes)
{
pStrings.Add(nd.FullPath);
if (nd.Nodes.Count>0)
{
this.AddNodes(nd.Nodes,pStrings);
}
}
}
}
}
TestCode:
private void Form1_Load(object sender, System.EventArgs e)
{
TreeNode ndA1=new TreeNode("A1");
this.treeView1.Nodes.Add(ndA1);
TreeNode ndA2=new TreeNode("A2");
this.treeView1.Nodes.Add(ndA2);
TreeNode ndB1=new TreeNode("B1");
ndA2.Nodes.Add(ndB1);
TreeNode ndB2=new TreeNode("B2");
ndA2.Nodes.Add(ndB2);
TreeNode ndC1=new TreeNode("C1");
ndB2.Nodes.Add(ndC1);
TreeNode ndC2=new TreeNode("C2");
ndB2.Nodes.Add(ndC2);
}
private void button1_Click(object sender, System.EventArgs e)
{
ArrayList arr=this.treeView1.SaveToStringArrayList();
this.treeView1.LoadFromStringArrayList(arr);
}
Dies ist eine von vielen Varianten wie man die Position in einer hierarchische Struktur in einem einzigen Feld (z.B: DB) abgespeichert werden kann....
Ausgabe:
ParamString: A1 ParentPath: Erstelle Node: A1 auf Parent: Key: A1\
ParamString: A2 ParentPath: Erstelle Node: A2 auf Parent: Key: A2\
ParamString: A2\B1 ParentPath: Node gefunden: A2\ rekursiver call mit RestString B1
ParamString: B1 ParentPath: A2\ Erstelle Node: B1 auf Parent: A2\ Key: A2\B1\
ParamString: A2\B2 ParentPath: Node gefunden: A2\ rekursiver call mit RestString B2
ParamString: B2 ParentPath: A2\ Erstelle Node: B2 auf Parent: A2\ Key: A2\B2\
ParamString: A2\B2\C1 ParentPath: Node gefunden: A2\ rekursiver call mit RestString B2\C1
ParamString: B2\C1 ParentPath: A2\ Node gefunden: A2\B2\ rekursiver call mit RestString C1
ParamString: C1 ParentPath: A2\B2\ Erstelle Node: C1 auf Parent: A2\B2\ Key: A2\B2\C1\
ParamString: A2\B2\C2 ParentPath: Node gefunden: A2\ rekursiver call mit RestString B2\C2
ParamString: B2\C2 ParentPath: A2\ Node gefunden: A2\B2\ rekursiver call mit RestString C2
ParamString: C2 ParentPath: A2\B2\ Erstelle Node: C2 auf Parent: A2\B2\ Key: A2\B2\C2\
PS: Hoffentlich wird der Backslash-Fehler des Boards irgendwann behoben 😉
Edit: Bitte beachten: Als dieser Beitrag geschrieben wurde, kannte ich erst .Net 1.1 also noch ohne Generics... heute sollte man Generics statt Hashtable verwenden 🙂
Früher war ich unentschlossen, heute bin ich mir da nicht mehr so sicher...