![]() |
Firemonkey Treeview mit Daten aus ClientDataSet füllen
Liste der Anhänge anzeigen (Anzahl: 1)
Moinsen :)
Ich stehe vor einem kleinen Problem. Ich glaube es ist garnicht mal so schwer zu lösen aber irgendwie habe ich eine Denkblokade... Ich komme einfach nicht weiter :cry: Ich habe am Rand meiner Firemonkey/FireUI Test App eine TTreeView Komponente. Diese möchte ich nun zur Laufzeit mit Daten aus einem TClientDataSet füllen. Allerdings bekomme ich das nicht ganz hin. Bevor etwas Code kommt, noch ein bisschen Theorie. Ein Datensatz im DataSet sieht so aus: "Jahr; KW*; Tag; ID; { ... }". *KW = Kalenderwoche. Jetzt möchte ich eine Hierarchie ausgehend von diesen Feldern erstellen. Nehmen wir an der erste Datensatz sieht so aus: "2014; 43; 2; 1;" (Der Tag wird ebenfalls als Zahl gespeichert, also 2 = Dienstag). Dementsprechend soll mein Treeview dann so aussehen: "2014" -> "37" -> "2" Wenn die nächsten beiden Datensätze nun so aussehen: "2014; 43; 2; 2;" und "2014; 43; 2; 3;" (Gleiches Jahr/KW/Tag), sollen diese dem TreeView an entsprechender Stelle angefügt werden. Da Jahr, Kalenderwoche und der Tag im TreeView schon existieren, werden die beiden ID's also die Childs von "2" (Dienstag):-> "1" "2014" -> "37" -> "2" -> "1" -> "2" Genauso verhält sich das auch mit den anderen Feldern. Wenn der Tag/KW/Jahr noch nicht im TreeView existiert, wird er eingefügt, so das sich eine Struktur wie diese ergibt:-> "3" "2014" -> "37" -> "2" -> "1" -> "2" "2015"-> "3" -> "1" -> "1" -> "1" -> "2" -> "1" { ... }-> "2" Ich hoffer man versteht meine super-dolle Erklärung 8-) So... Nach professioneller Erklärung und Schemazeichnung folgt jetzt Amateurhafter Schüler-Code :roll:
Delphi-Quellcode:
Ich habe im Anhang nochmal ein Demo-Programm zusammengenagelt inklusive Quellen. Es ist mit der XE7 Trial erstellt worden, deshalb weiß ich nicht wie es mit der Abwärtskompatibilität aussieht :glaskugel: Wenn abwärtskompatibel, dann wohl ab XE2, da LiveBindings.
procedure TfrmMain.CreateTree;
var TreeItems: array of TTreeViewItem; LetztesJahr, LetzteKW, LetzterTag: TTreeViewItem; function ItemExists(AText: String): Boolean; var I: Integer; begin Result := False; if Length(TreeItems) > 0 then for I := Low(TreeItems) to High(TreeItems) do if TreeItems[I].Text = AText then begin Result := True; Break; end; end; begin ClientDataSet1.First; while not ClientDataSet1.Eof do begin // Jahr if not ItemExists(ClientDataSet1.Fields[0].AsString) then // Wenn der Text noch nicht im TreeView ist (nich keinem Item zugewießen)... begin SetLength(TreeItems, Length(TreeItems) + 1); // Item-Array erweitern TreeItems[High(TreeItems)] := TTreeViewItem.Create(Self); // Item erzeugen TreeItems[High(TreeItems)].Text := ClientDataSet1.Fields[0].AsString; // Item Text := Wert aus Feld TreeItems[High(TreeItems)].Parent := TreeView1; // Parent := TreeView1 TreeItems[High(TreeItems)].Expand; // Ausklappen LetztesJahr := TreeItems[High(TreeItems)]; // siehe bei KW end; // KW if not ItemExists(ClientDataSet1.Fields[1].AsString) then // Wenn der Text noch nicht im TreeView ist (nich keinem Item zugewießen)... begin SetLength(TreeItems, Length(TreeItems) + 1); // Item-Array erweitern TreeItems[High(TreeItems)] := TTreeViewItem.Create(Self); // Item erzeugen TreeItems[High(TreeItems)].Text := ClientDataSet1.Fields[1].AsString; // Item Text := Wert aus Feld TreeItems[High(TreeItems)].Parent := LetztesJahr; // Parent := Letztes Jahr TreeItems[High(TreeItems)].Expand; // Ausklappen LetzteKW := TreeItems[High(TreeItems)]; // siehe bei Tag end; // Tag if not ItemExists(ClientDataSet1.Fields[2].AsString) then // Wenn der Text noch nicht im TreeView ist (nich keinem Item zugewießen)... begin SetLength(TreeItems, Length(TreeItems) + 1); // Item-Array erweitern TreeItems[High(TreeItems)] := TTreeViewItem.Create(Self); // Item erzeugen TreeItems[High(TreeItems)].Text := ClientDataSet1.Fields[2].AsString; // Item Text := Wert aus Feld TreeItems[High(TreeItems)].Parent := LetzteKW; // Parent := Letzte Kalenderwoche TreeItems[High(TreeItems)].Expand; // Ausklappen LetzterTag := TreeItems[High(TreeItems)]; // siehe bei ID end; // ID if not ItemExists(ClientDataSet1.Fields[3].AsString) then // Wenn der Text noch nicht im TreeView ist (nich keinem Item zugewießen)... begin SetLength(TreeItems, Length(TreeItems) + 1); // Item-Array erweitern TreeItems[High(TreeItems)] := TTreeViewItem.Create(Self); // Item erzeugen TreeItems[High(TreeItems)].Text := ClientDataSet1.Fields[3].AsString; // Item Text := Wert aus Feld TreeItems[High(TreeItems)].Parent := LetzterTag; // Parent := Letzter Tag TreeItems[High(TreeItems)].Expand; // Ausklappen end; ClientDataSet1.Next; // Weiter und wieder von vorne für nächsten Datensatz... end; { if Length(TreeItems) = 0 then TreeView1.ExpandAll; } end; Auf jeden Fall haut mein Code nicht hin, und ich kreige einfach nich raus woran es liegt. Im Kopf habe ich mir das alles einfach zurechtgelegt, aber natürlich funktioniert es nie so wie man will. Ich hoffe ihr könnt mir helfen, denn ich zerbreche mir hier schon den ganzen Abend den Kopf. :evil: Gruß * Ein Fehler ist mir schonmal ins Netz gegangen: Die if-Abfrage haut nicht hin, den die Funktion ItemExists gibt ja schon True zurück, wenn nur ein Item mit dem selben Text existert. Das heißt wenn ich einmal das Feld ID mit dem Wert 1 hinzugefügt habe, geht das dannach nicht mehr. Obwohl es ja mehrmals vorkommen kann/soll. ** Anhang vergessen, ist jetzt dabei. |
AW: Firemonkey Treeview mit Daten aus ClientDataSet füllen
Und so? :stupid:
Delphi-Quellcode:
procedure TForm1.PresentDataset;
var LTreeDict: TDictionary<string, TTreeViewItem>; LTreeViewItem: TTreeViewItem; LParent: TFmxObject; LKey: string; begin LTreeDict := TDictionary<string, TTreeViewItem>.Create( ); try TreeView1.BeginUpdate; try TreeView1.Clear; ClientDataSet1.First; while not ClientDataSet1.Eof do begin LParent := TreeView1; LKey := ClientDataSet1Jahr.AsString; // z.B. 2013 if not LTreeDict.TryGetValue( LKey, LTreeViewItem ) then begin LTreeViewItem := TTreeViewItem.Create( nil ); LTreeViewItem.Parent := LParent; LTreeViewItem.Text := ClientDataSet1Jahr.AsString; LTreeDict.Add( LKey, LTreeViewItem ); end; LParent := LTreeViewItem; LKey := LKey + '\' + ClientDataSet1KW.AsString; // z.B. 2013\44 if not LTreeDict.TryGetValue( LKey, LTreeViewItem ) then begin LTreeViewItem := TTreeViewItem.Create( nil ); LTreeViewItem.Parent := LParent; LTreeViewItem.Text := 'KW' + ClientDataSet1KW.AsString; LTreeDict.Add( LKey, LTreeViewItem ); end; LParent := LTreeViewItem; LKey := LKey + '\' + ClientDataSet1Tag.AsString; // z.B. 2013\44\28 if not LTreeDict.TryGetValue( LKey, LTreeViewItem ) then begin LTreeViewItem := TTreeViewItem.Create( nil ); LTreeViewItem.Parent := LParent; LTreeViewItem.Text := FormatDateTime( 'DD.MM. dddd', ClientDataSet1Datum.AsDateTime ); LTreeDict.Add( LKey, LTreeViewItem ); end; LParent := LTreeViewItem; LKey := LKey + '\' + ClientDataSet1ID.AsString; // z.B. 2013\44\28\5 if not LTreeDict.TryGetValue( LKey, LTreeViewItem ) then begin LTreeViewItem := TTreeViewItem.Create( nil ); LTreeViewItem.Parent := LParent; LTreeViewItem.Text := ClientDataSet1ID.AsString; LTreeDict.Add( LKey, LTreeViewItem ); end; ClientDataSet1.Next; end; finally TreeView1.EndUpdate; end; finally LTreeDict.Free; end; end; |
AW: Firemonkey Treeview mit Daten aus ClientDataSet füllen
@Sir: Ich hatte erst übersehn daß LParent mittendrin immer wieder neu gesetzt wird und dachte das steht alles auf der ersten Zuweisung (TreeView1), anstatt auf den jeweiligen Knoten. :oops:
Sieht aber OK aus und der Fehler vom TE ist auch behoben. z.B. gibt es den selben Tag (gleicher Wert) in unterschieglichen Wochen und Jahren, womit man also nicht nur über den Tag als einzelnes suchen kann, sondern nur im Zusammenhang mit dessen übergeordneten Knoten. Und ich hätte gedacht, daß man dem komischen TreeView auch die "coolen" Livebindings verpasst hätte. Dann bräuchte man in seinem DataSet nur zwei Spalten mit ID und ParentID, worüber der View seinen Tree selber aufbaut. (eventuell müssten nur noch die fehltenden Baumknoten, für Jahr/KW/Tag, anhand der bestehenden Daten berechnet und eingefügt werden) |
AW: Firemonkey Treeview mit Daten aus ClientDataSet füllen
Das ist doch das Prinzip von LibeBindings, dass man keine speziellen Komponenten dafür benötigt, sondern so ziemlich alles an ziemlich alles binden kann.
|
AW: Firemonkey Treeview mit Daten aus ClientDataSet füllen
@SirRufo: Oh... danke :P Hatte nicht damt gerechnet das das so schnell klappt ^^ Funktioniert. Hatte beim googlen sogar irgendwas von Generics gelesen. Dachte mir aber das wäre wieder zu Zeitaufwendig sich das anzuschauen, für die paar kleinigkeiten, die ich umsetze. Wieder was für meine To-Do Liste :thumb:
@himitsu, mkinzler: Ich benutze eigentlich keine LiveBindings. Ich habe in der Demoanwendung nur schnell den Navigator und das Grid mit LiveBindings an das ClientDataSet gebunden. In der Anwendung um die es eigentlich geht, wird das ClientDataSet zur Laufzeit in einer anderen Klasse erstellt. Und da ist mir das mit den LiveBindings wieder zu kompliziert. 8-) |
AW: Firemonkey Treeview mit Daten aus ClientDataSet füllen
Zitat:
|
AW: Firemonkey Treeview mit Daten aus ClientDataSet füllen
Moin,
ich muss doch nochmal nachhacken. Wenn ich den Tree während der Lauzeit aktualisiere geht ja die Selektion verloren. Ist ja auch logisch. Jetzt habe ich mir das selektierte Item vor dem aktualisieren in einer Variable gespeichert, bzw. im Dictionary und anschließend wieder an die Selected Eigenschaft des Tree's übergeben. Allerdings liegt die Selektion im Tree dann bei einem anderen Item :| Nicht immer, aber in den meisten Fällen, das scheint zufällig zu sein. Aber wenn ich den Tree aus immer den gleichen Daten erstelle, dürfte sich doch eigentlich garnichts ändern :?: Oder liege ich da falsch? |
AW: Firemonkey Treeview mit Daten aus ClientDataSet füllen
Zitat:
Du meinst doch nicht die Objektreferenz, denn die kann wird natürlich anders sein, wenn man das Ding neu aufbaut, und je nach Komponente kann sich eine Referenz auch ändern, wenn beim Ändern die internen Komponenten neu aufgebaut/verlinkt werden. :stupid: |
AW: Firemonkey Treeview mit Daten aus ClientDataSet füllen
Eine kleine Pfadbehandlung und schon kann man mit einer Funktion beliebig tiefe Pfade im Tree suchen und/oder erstellen.
Delphi-Quellcode:
Aber ich merk immer mehr, daß dieser TreeView einfach nur ein totaler Schrotthaufen ist.
function TForm1.FindOrCreateItem(ItemParent: TFmxObject; ItemPath: string; InsertDuplicate: Boolean=False): TTreeViewItem;
var i: Integer; begin if ContainsStr(ItemPath, PathDelim) then ItemParent := FindOrCreateItem(ItemParent, ExtractFileDir(ItemPath), False); ItemPath := ExtractFileName(ItemPath); if not InsertDuplicate then if ItemParent is TCustomTreeView then begin for i := TCustomTreeView(ItemParent).Count downto 0 do begin Result := TCustomTreeView(ItemParent).Items[i]; if SameStr(Result.Text, ItemPath) then Exit; end; end else if ItemParent is TTreeViewItem then begin for i := TTreeViewItem(ItemParent).Count downto 0 do begin Result := TTreeViewItem(ItemParent).Items[i]; if SameStr(Result.Text, ItemPath) then Exit; end; end else raise Exception.Create('Ungültiger Parent-Typ.'); Result := TTreeViewItem.Create(ItemParent); Result.Parent := ItemParent; Result.Text := ItemPath; end; procedure TForm1.PresentDataset; var ItemPath: string; begin TreeView1.BeginUpdate; try TreeView1.Clear; ClientDataSet1.First; while not ClientDataSet1.Eof do begin ItemPath := ClientDataSet1Jahr.AsString + PathDelim + ClientDataSet1KW.AsString + PathDelim + ClientDataSet1Tag.AsString + PathDelim + ClientDataSet1ID.AsString; with FindOrCreateItem(TreeView1, ItemPath, True) do begin // True = Datensätze mit doppelter "ID" auch mehrfach einfügen //Text := ClientDataSet1ID.AsString; // hat FindOrCreateItem bereits gesetzt Tag := ClientDataSet1.RecNo; // so hätte man einen Zugang vom Item zu seinem DataRecord end; ClientDataSet1.Next; end; finally TreeView1.EndUpdate; end; end;
|
AW: Firemonkey Treeview mit Daten aus ClientDataSet füllen
Ach ich bin so ein Trottel, natürlich hatte ich die Objektreferenz gespeichert :oops::oops:
Ich habe es jetzt ganz anders gelöst: Ich erstelle mir den Key des selektierten Items (also pracktisch Rufo's Routine rückwärts - den Key zusammensetzen), und am Schluss muss ich nur noch im Dictionary schauen welches Item den Key besitzt (den der ist ja einmalig) und dieses Item wird dann selektiert :thumb: |
Alle Zeitangaben in WEZ +1. Es ist jetzt 11:09 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz