AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Programmieren allgemein Firemonkey Treeview mit Daten aus ClientDataSet füllen

Firemonkey Treeview mit Daten aus ClientDataSet füllen

Ein Thema von ByTheTime · begonnen am 22. Okt 2014 · letzter Beitrag vom 24. Okt 2014
Antwort Antwort
ByTheTime

Registriert seit: 24. Sep 2011
Ort: Frankfurt
297 Beiträge
 
Delphi XE2 Architect
 
#1

Firemonkey Treeview mit Daten aus ClientDataSet füllen

  Alt 22. Okt 2014, 21:41
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

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"
-> "1"
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):

"2014"
-> "37"
-> "2"
-> "1"
-> "2"
-> "3"
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:


"2014"
-> "37"
-> "2"
-> "1"
-> "2"
-> "3"
"2015"
-> "1"
-> "1"
-> "1"
-> "2"
-> "1"
-> "2"
{ ... }

Ich hoffer man versteht meine super-dolle Erklärung

So... Nach professioneller Erklärung und Schemazeichnung folgt jetzt Amateurhafter Schüler-Code

Delphi-Quellcode:
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;
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 Wenn abwärtskompatibel, dann wohl ab XE2, da LiveBindings.

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.

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.
Angehängte Dateien
Dateityp: zip TreeView Test.zip (3,21 MB, 3x aufgerufen)
Lukas

Geändert von ByTheTime (22. Okt 2014 um 22:14 Uhr) Grund: Anhang vergessen
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#2

AW: Firemonkey Treeview mit Daten aus ClientDataSet füllen

  Alt 23. Okt 2014, 01:25
Und so?
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;
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)

Geändert von Sir Rufo (23. Okt 2014 um 01:27 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
35.439 Beiträge
 
Delphi 10.3 Rio
 
#3

AW: Firemonkey Treeview mit Daten aus ClientDataSet füllen

  Alt 23. Okt 2014, 09:50
@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.

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)
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
Delphi-Tage 2005-2014
  Mit Zitat antworten Zitat
mkinzler
(Moderator)

Registriert seit: 9. Dez 2005
Ort: Heilbronn
39.537 Beiträge
 
Delphi 10.2 Tokyo Enterprise
 
#4

AW: Firemonkey Treeview mit Daten aus ClientDataSet füllen

  Alt 23. Okt 2014, 09:53
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.
Markus Kinzler
  Mit Zitat antworten Zitat
ByTheTime

Registriert seit: 24. Sep 2011
Ort: Frankfurt
297 Beiträge
 
Delphi XE2 Architect
 
#5

AW: Firemonkey Treeview mit Daten aus ClientDataSet füllen

  Alt 23. Okt 2014, 11:00
@SirRufo: Oh... danke 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

@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.
Lukas
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#6

AW: Firemonkey Treeview mit Daten aus ClientDataSet füllen

  Alt 23. Okt 2014, 11:26
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.
Wenn die LB wirklich zuverlässig funktionieren würden, dann wäre das ja auch kein Problem ... tun sie aber nicht ... dann doch lieber von Hand
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)
  Mit Zitat antworten Zitat
ByTheTime

Registriert seit: 24. Sep 2011
Ort: Frankfurt
297 Beiträge
 
Delphi XE2 Architect
 
#7

AW: Firemonkey Treeview mit Daten aus ClientDataSet füllen

  Alt 23. Okt 2014, 18:11
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?
Lukas

Geändert von ByTheTime (23. Okt 2014 um 18:16 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
35.439 Beiträge
 
Delphi 10.3 Rio
 
#8

AW: Firemonkey Treeview mit Daten aus ClientDataSet füllen

  Alt 23. Okt 2014, 18:50
Zitat:
Jetzt habe ich mir das selektierte Item vor dem aktualisieren in einer Variable gespeichert
Wie, bzw. was hast du gespeichert?

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.
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
Delphi-Tage 2005-2014

Geändert von himitsu (23. Okt 2014 um 19:12 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
35.439 Beiträge
 
Delphi 10.3 Rio
 
#9

AW: Firemonkey Treeview mit Daten aus ClientDataSet füllen

  Alt 23. Okt 2014, 19:44
Eine kleine Pfadbehandlung und schon kann man mit einer Funktion beliebig tiefe Pfade im Tree suchen und/oder erstellen.
Delphi-Quellcode:
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;
Aber ich merk immer mehr, daß dieser TreeView einfach nur ein totaler Schrotthaufen ist.
  • Es gibt keinen (unsichtbaren) Root-Knoten, womit man in dem Baum den Hauptknoten und die Unterknoten unterschiedlich behandeln muß, obwohl der Code gleich aussieht.
  • Das public ItemByIndex(i) ist ja sowas von sinnlos und ja genau dem Items[i], aber es gibt keinerlei Funktionen zum Suchen von Items, vorallem nicht über den Text.
  • Selbst der uralte VCL-TreeView ist dem Ding haushoch überlegen und hat unmassen mehr Features. (angefangen bei den Icons und einer Sortierung)
  • Einfügen an beliebiger Stelle (Insert mit Index) und Verschieben von Knoten.
  • Und LiveBindings hat man hier total vergessen, obwohl man dafür so viel Werbung macht und meint wie cool die doch seinen. (nja, an allen Ecken haben die sowieso enorme Bugs und sind nicht wirklich benutzbar)
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
Delphi-Tage 2005-2014

Geändert von himitsu (23. Okt 2014 um 19:46 Uhr)
  Mit Zitat antworten Zitat
ByTheTime

Registriert seit: 24. Sep 2011
Ort: Frankfurt
297 Beiträge
 
Delphi XE2 Architect
 
#10

AW: Firemonkey Treeview mit Daten aus ClientDataSet füllen

  Alt 24. Okt 2014, 01:35
Ach ich bin so ein Trottel, natürlich hatte ich die Objektreferenz gespeichert
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
Lukas
  Mit Zitat antworten Zitat
Themen-Optionen Thema durchsuchen
Thema durchsuchen:

Erweiterte Suche
Ansicht

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 03:42 Uhr.
Powered by vBulletin® Copyright ©2000 - 2020, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2019 by Daniel R. Wolf