Laden...

TreeView abhängig vom Verzeichnislevel füllen

Erstellt von Schattenkanzler vor 18 Jahren Letzter Beitrag vor 16 Jahren 21.054 Views
S
Schattenkanzler Themenstarter:in
238 Beiträge seit 2004
vor 18 Jahren
TreeView abhängig vom Verzeichnislevel füllen

Hallöle!

Ich weiß nicht, der wievielte Thread zu TreeViews und Rekursion das ist, aber ich dreh langsam echt durch...und die Suche konnte mir meine Frage leider auch nicht beantworten.

Also, folgendes Problem: Ich erhalte über einen HTTPWebRequest eine Datei, in der jede Menge Dateinamen inkl. Verzeichnis stehen.
Aus dieser Datei filtere ich individuelle Verzeichnisnamen aus, so dass jedes Verzeichnis nur einmal dabei ist.
Nun möchte ich diese Verzeichnisse als children eines einzelnen TreeNodes anzeigen, abhängig von deren Verzeichnislevel. Beispiel:

[PRE]RootNode
 |_ Dir
     |_Dir\Dir
        |_Dir\Dir\Dir
 |_Dir
    |_Dir\Dir
       |_Dir\Dir\Dir
          |_Dir\Dir\Dir\Dir
usw.[/PRE]

Natürlich sollte dabei immer nur der Name des letzten Verzeichnisses angezeigt werden (alá Explorer).
Aus

[PRE]RootNode
 |_ Dir1
     |_Dir2\Dir2a
        |_Dir3\Dir3a\Dir3b[/PRE]

sollte also

[PRE]RootNode
 |_ Dir1
     |_Dir2a
        |_Dir3b[/PRE]

werden.

Leider habe ich keinen Schimmer, wie ich das anstellen soll.
Habe schon eine Klasse von System.Windows.Forms.TreeNode abgeleitet, die als zusätzlichen Wert die Anzahl der "" enthält, um so das Level bestimmen zu können, allerdings hat mich das auch nicht wirklich weitergebracht.

Warum krieg ich die Rekursion nicht in meinen Schädel?!? Gibt's dazu nicht mal ein gutes Tutorial?

Wie dem auch sei, ich hoffe, jemand kann mir dabei helfen...

Ich hänge mal eine Datei an, die ein paar fiktive Verzeichnisse enthält. Die Ausgabe sollte so aussehen wie hier:

[PRE]Root
 |_ D:\
     |_Eigene Dateien
        |_Eigene Downloads
           |_Multi
              |_ CD 1
              |_ CD 2
              |_ CD 3
                  |_ Teil 2
              |_ CD 4
        |_Eigene Musik
     |_ Programme
         |Games[/PRE]

So, ich denke, ich habe mein Problem geschildert...
Würde mich freuen, wenn sich jemand meines Falles annehmen könnte...

Greets - SK

P.S: Am liebsten wäre mir natürlich ein Code-Snippet oder ein vollständiger Code, allerdings würde ich mich noch mehr über zahlreiche Kommentare freuen - will es schließlich endlich mal raffen!

Sagte ich schon Danke? Nein? ...kommt noch...

49.485 Beiträge seit 2005
vor 18 Jahren

Hallo Schattenkanzler,

erstmal: Rekusion ist cool!

Wenn ich das richtig verstehe ist dein Problem, dass die Unterverzeichnisse nicht als Unterknoten erscheinen, sondern alles flach auf Ebene 0 eingefügt wird. Der Trick ist einfach den Unterknoten demjenigen Knoten hinzuzufügen, unter dem er erscheinen soll:


tnEbene0 = new TreeNode ("Ebene 0");
tnEbene1 = new TreeNode ("Ebene 1");
tnEbene2 = new TreeNode ("Ebene 2");
// Bis hier sind das alles gleichberechtigte Knoten
// aber jetzt bringen wir Struktur rein:
tv.Nodes.Add (tnEbene0);
tnEbene0.Nodes.Add (tnEbene1);
tnEbene1.Nodes.Add (tnEbene2);

herbivore

4.221 Beiträge seit 2005
vor 18 Jahren

Nicht dass Du noch verzweifelst.....

Rekursion war mir zu einfach.... es geht auch ohne Rekursion

z.B: so


		private void button1_Click(object sender, System.EventArgs e)
		{
			string[] lines=new string[]{
				@"D:\Eigene Dateien",
				@"D:\Programme\Games",
				@"D:\Eigene Dateien\Eigene Musik2",
				@"D:\",
				@"D:\Eigene Dateien\Eigene Downloads\Multi",
				@"D:\Eigene Dateien\Eigene Downloads\Multi\CD 1",
				@"D:\Eigene Dateien\Eigene Downloads\Multi\CD 2",
				@"D:\Eigene Dateien\Eigene Downloads\Multi\CD 3",
				@"D:\Eigene Dateien\Eigene Downloads\Multi\CD 3\Teil 2",
				@"D:\Eigene Dateien\Eigene Downloads\Multi\CD 4",
			};

			SortedList sl=new SortedList();
			this.treeView1.Nodes.Clear();
			this.treeView1.BeginUpdate();
			TreeNodeCollection parentNodes=this.treeView1.Nodes;
			foreach (string line in lines)
			{
				string[] stringParts=line.Split('\\');

				TreeNode nd=null;
				string strParentKey=string.Empty;
				foreach (string s in stringParts)
				{
					
					string sTrimmed=s.Trim();
					if (sTrimmed==string.Empty)
					{
						continue;
					}
					if (strParentKey.Length>0)
					{
						strParentKey=string.Concat (strParentKey,@"\",sTrimmed);
					}
					else
					{
						strParentKey=string.Concat (strParentKey,sTrimmed);
					}
				
					if (sl.ContainsKey(strParentKey))
					{
						//verwende diesen
						nd=sl[strParentKey] as TreeNode;
					}
					else
					{
						//create new
						nd=new TreeNode(sTrimmed);
						//den FullPath adden
						sl.Add(strParentKey,nd);
						parentNodes.Add(nd);
					}
					parentNodes=nd.Nodes;
				}
			}
			this.treeView1.EndUpdate();
		}
	}


Nur so als Tipp am Rande...

Mit Rekursion wirst Du auf die Nase fallen weil die Liste nicht sortiert ist... Rekursion funzt nur wenn die Nodes in der richtigen Reihenfolge einlaufen...

Beschränkung des geposteten Codes.... wenn mehr als ein Drive betroffen ist, dann wird der Code nicht funzen.... man müsste diesen dann dahingehend abändern dass die Variable parentNodes bei der Erstellung des Drive-Nodes wieder auf this.treeview1.Nodes verweist...

Früher war ich unentschlossen, heute bin ich mir da nicht mehr so sicher...

S
Schattenkanzler Themenstarter:in
238 Beiträge seit 2004
vor 18 Jahren

@Programmierhans: Die Ausgabe deines Codes sieht...ziemlich komisch aus...
Ich zeig's mal: [siehe Anhang]

Aber an sich ist es schon ziemlich nah dran...

@herbivore: Den Trick kenne ich natürlich, allerdings krieg ich es nicht vernünftig implementiert.
Entweder fehlen 90% der Knoten, ich kriege eine Stack-Fehlermeldung oder noch besser eine "Konnte Ressourcen nicht laden"-Meldung...

Würde mich dementsprechend über erneute Hilfe freuen!

Danke schonmal an alle!

Greets - SK

Sagte ich schon Danke? Nein? ...kommt noch...

49.485 Beiträge seit 2005
vor 18 Jahren

Hallo Schattenkanzler,


Dictionary <String, TreeNode> dictNode = new Dictionary <String, TreeNode> ();

using (StreamReader sr = new StreamReader ("data.txt", Encoding.Default)) {
   String strDir;
   while ((strDir = sr.ReadLine ()) != null) {
      String [] astrPart = strDir.Split ('\\\\');
      String strBegin = "";
      TreeNode tnParent = null;
      foreach (String strPart in astrPart) {
         if (strPart == "") {
            break;
         }
         strBegin += "\\\\" + strPart;
         if (dictNode.ContainsKey (strBegin)) {
            tnParent = dictNode [strBegin];
            continue;
         }
         dictNode [strBegin] = new TreeNode (strPart);
         if (tnParent == null) {
            _tvDir.Nodes.Add (dictNode [strBegin]);
         } else {
            tnParent.Nodes.Add (dictNode [strBegin]);
         }
         tnParent = dictNode [strBegin];
      }
   }
}

Das Dictionary kannst du durch eine Hashtable ersetzen, wenn du mit 1.1 arbeitetst.

herbivore

PS: Ist ja lustig. Ich hatte mir den Code von dir, Programmierhans, gar nicht angeguckt. Aber die Parallelen sind unverkennbar. Es gibt halt bestimmte Herangehensweisen, die sich anbieten 🙂

Das mit dem Trim hatte ich auch überlegt, aber dann gelassen. Und zuerst hatte ich bei der Abfrage 'strPart == ""' auch ein continue, dass ich erst beim Posten durch ein break ersetzt habe.

PPS: Es bietet sich an, was man strBegin anhängt, in Kleinbuchschreibung zu konvertieren.

S
Schattenkanzler Themenstarter:in
238 Beiträge seit 2004
vor 18 Jahren

Ok, jetzt dreht sich bei mir alles!
Ich habe gerade versucht, einfach den Code per Copy & Paste einzubauen, dabei kam dann das hier raus:

MainForm.cs(498,21): error CS1002: ; expected
MainForm.cs(498,21): error CS1525: Invalid expression term ','
MainForm.cs(498,23): error CS1002: ; expected
MainForm.cs(498,58): error CS1526: A new expression requires () or [] after type
MainForm.cs(498,65): error CS1002: ; expected
MainForm.cs(498,65): error CS1525: Invalid expression term ','
MainForm.cs(498,67): error CS1002: ; expected
MainForm.cs(498,78): error CS1525: Invalid expression term ')'
MainForm.cs(498,79): error CS1026: ) expected

Abgeschlossen -- 9 Fehler, 0 Warnungen

Ich benutze Framework 1.1 und habe aus "Dictionary" einfach "HashTable" gemacht...
Was muss ich noch abändern/einbinden, damit das klappt?

Greets - SK

P.S: Die erste Zeile deines Codes ist bei mir Zeile 498...

Sagte ich schon Danke? Nein? ...kommt noch...

49.485 Beiträge seit 2005
vor 18 Jahren

Hallo Schattenkanzler,

ich bin hin- und hergerissen, zwischen Frust und Hilfsbereitschaft. Jetzt poste ich schon funktionierenden (ok 2.0-) Code und du musst du nur 2x "Dictionary <String, TreeNode>" durch "Hashtable" ersetzen (die spitzen Klammern müssen weg, das ist ja gerade, was 1.1 nicht kann: generische Type) und dann vier Casts schreiben. Das hat nun mit fehlendem Verständnis bezüglich Rekursion gar nichts zu tun, sondern ist simples Beheben von Complierfehlern. Ich denke das solltest du alleine hinbekommen - zumal ich ja jetzt doch verraten habe, was zu tun ist.

herbivore

PS: Ich hätte eigentlich sogar erwartet, dass du versuchst, den Code zu verstehen, um deine Probleme mit Rekursion bzw. rekursiven Strukturen zu überwinden. Zumal du ja gelesen hast, dass man den Code quasi als (einen) Standardalorithmus ansehen kann.

4.221 Beiträge seit 2005
vor 18 Jahren

Mach die \\ bei den Pfaden wieder rein.... scheinbar unterdrückt der Browser gewisse Escapes..

Früher war ich unentschlossen, heute bin ich mir da nicht mehr so sicher...

S
Schattenkanzler Themenstarter:in
238 Beiträge seit 2004
vor 18 Jahren

@herbivore: Ich glaube, ich bin an der Zeile

Dictionary <String, TreeNode> dictNode = new Dictionary <String, TreeNode> ();

gescheitert. Bin noch nicht so bewandert in C#, konnte mit den Klammern rein gar nix anfangen...und war entsprechend verwirrt.

So, jetzt hab ich meinen (zugegebenermaßen momentan etwas faulen*) Arsch mal hingehockt und den Code entsprechend angepasst. Und siehe da. Er funktioniert perfekt! 😁
Da freu ich mich!
Nur wirklich verstanden hab ich ihn noch nicht...glaub ich...
Hier ist er erstmal so, wie er bei mir ausführbar ist:

//Hashtable erzeugen
			Hashtable dictNode = new Hashtable ();
			
			//Datei auslesen...
			using (StreamReader sr = new StreamReader (Application.StartupPath + "\\Data\\data.txt", Encoding.Default))
			{
   				string readDir;
   				
  				while ((readDir = sr.ReadLine ()) != null)
  				{
  					//Ausgelesene Zeile bei "\" splitten:
      				string[] splittedPath = readDir.Split ('\\');
      				
     				string strBegin = "";
    				
     				TreeNode parentNode = null;
     				
     				foreach (string pathSplitter in splittedPath)
     				{
     					//Wenn das Element leer ist, abbrechen
      				   	if (pathSplitter == "")
      				   	{
         					break;
        			   	}
        			   
      				   	strBegin += "\\" + pathSplitter;
         			  
      				   	if (dictNode.ContainsKey (strBegin))
         			  	{
      				   		parentNode = (TreeNode)dictNode [strBegin];
      				   		continue;
      				    }
      					
         			 	dictNode [strBegin] = new TreeNode (pathSplitter);
         
         			   	if (parentNode == null) 
         			   	{
         			   		this.tvRemoteDirs.Nodes.Add((TreeNode)dictNode [strBegin]);
         			   	} 
         				else 
         			   	{
         			   		parentNode.Nodes.Add ((TreeNode)dictNode [strBegin]);
         			   	}
       				 
         			   parentNode = (TreeNode)dictNode [strBegin];
     				}
   				}
			}

(Hmm, Board versaut grad die Formatierung, sollte aber nicht weiter stören...)

Kann ich dich noch mal bitten, das Ganze mit dem ein oder anderen Kommentar zu versehen?

Bis zur Zeile

strBegin += "\\" + pathSplitter;

raff ich's noch, aber danach ist echt Ende bei mir (da ich mich beispielsweise mit Hashtables absolut nicht auskenne...

Also...wäre nett...danke nochmal!

Greets - SK

P.S: Das Ganze als Pseudo-Code wäre z.B. sehr herrlich...also so alá

WENN DerAktuelleString == SoUndSo DANN
{
MachDiesesUndJenes
}

P.P.S: *: Muss momentan Schmerzmittel schlucken, da ich mir ziemlich fies die Pfote verbrannt hab. Die Dinger machen mehr als nur langsam im Kopf...keine Ausrede, nur eine Erklärung...

Sagte ich schon Danke? Nein? ...kommt noch...

49.485 Beiträge seit 2005
vor 18 Jahren

Hallo Schattenkanzler,

zu Hashtables siehe: Grundlegendes zu Hashtables

Die beiden Schleifen gehen einfach für jede Zeile in der Datei (Pfad) über seine einzenen Bestandsteile (Verzeichnisnamen). Für jedes Verzeichnis (das ja zu genau einem Knoten führen soll) wird geguckt, ob der Beginn des Pfades bis einschließlich dieses Verzeichnisse früher schon mal aufgetaucht ist. Dann ist der passende Knoten ja schon erzeugt und in der Hashtable gespeichert. Wenn nicht erzeugen wir einen neuen Knoten.

Das einzige was jetzt noch fehlt, ist die Stelle, wo wir den neuen Knoten einfügen müssen (also sein Parent). Der Parent ist immer der Knoten für das jeweils vorige Verzeichnis (welchen wir entweder im vorigen Schleifendurchlauf erzeugt haben, oder wenn er schon in der Hashtable gespeichert ist, aus dieser herausholen).

Die Hashtable merkt sind zu jedem Anfangsstück eines Pfades den dazu erzeugten Knoten. Man kann sie fragen, ob für einen bestimmten Pfadbeginn schon ein Eintrag vorhanden ist (ContainsKey). Außerdem kann man diesen Eintrag herausholen

parentNode = (TreeNode)dictNode [strBegin];

oder einen neuen Eintrag setzen

dictNode [strBegin] = new TreeNode (pathSplitter);

Der Parent ist für jeden neuen Laufwerksbuchstaben null. Entsprechend wird der Knoten für den Laufwerksbuchstaben nicht einem übergeordneten Knoten hinzugefügt (den es ja gerade nicht gibt), sondern direkt dem Treeview auf oberster Ebene.

herbivore

S
Schattenkanzler Themenstarter:in
238 Beiträge seit 2004
vor 18 Jahren

Ja, so langsam - wirklich langsam - wird das Bild deutlicher...muss ich mich wohl noch mal eingehender mit beschäftigen!

Danke auf jeden Fall und nix für ungut wegen meines unüberlegten Gemaules gestern 😁

Greets - SK

Sagte ich schon Danke? Nein? ...kommt noch...

S
Schattenkanzler Themenstarter:in
238 Beiträge seit 2004
vor 16 Jahren

Hey, herbivore!

Jetzt, mehr als zwei Jahre nach meiner Frage und deiner Antwort, hat mir dieses längst vergessene Posting erneut weitergeholfen (bin tatsächlich über die Suche drüber gestolpert!)!

Mittlerweile kenn ich mich mit der Hashtable weitaus besser aus als damals und auf einmal ergibt das alles auf Anhieb einen Sinn!

Danke nochmal! 😁

Greets - SK

Sagte ich schon Danke? Nein? ...kommt noch...