Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   GUI-Design mit VCL / FireMonkey / Common Controls (https://www.delphipraxis.net/18-gui-design-mit-vcl-firemonkey-common-controls/)
-   -   Delphi Stringgrid "schnell" beladen (https://www.delphipraxis.net/136354-stringgrid-schnell-beladen.html)

QuickAndDirty 29. Jun 2009 08:01


Stringgrid "schnell" beladen
 
Hallo liebe Mitdelphianer, *g*

heute geht es mir darum eine TStringgrid Instanz möglichst Effizient zu beladen...

JA ich weiß das ein TStringgrid für größere Datenmengen nicht wirklich gut
geeignet ist. Und Ich weiß das es Ideal wäre nur wenig mehr als den
angezeigten Bereich an Daten zu laden. Das ist jetzt nicht das Thema.

Wie also bekomme ich möglichst schnell ein Gitter mit 3 * ca. 16000
Zellen gefüllt?

Die 3 Datenfelder werden aus einer Datenbank gelesen und es können unterschiedlich
viele Zeilen/Datensätze sein.

-Ich dachte erst ich könnte mir zeitsparen wenn ich z.B. Das Gitter nicht immer
Zeile für Zeile vergrößere sondern einmal die nötige Größer ermittele und setzte...
leider dauert Dataset.recordcount auch lange und wird nicht überall unterstützt :(
-> Also könnte es Sinn machen einfach in großen Schritten den Rowcount zu erhöhen ? Kostet das überhaupt Zeit?

-Z.z. Wird Schreibend auf CELLS[c,r] zugegriffen. Ich habe es auch schon mal mit ROWS[r].commatext versucht, scheint
keinen Gewinn zu bringen(vielleicht weil das zusammen bauen und auseinander bauen der Feld Inhalte die Zeit wieder auffrisst).

-Rows[r].append würde ja nur sinn machen wenn das gitter dynamisch erweitert wird. Und ich weiß nicht ob es schneller ist.


habt ihr ne Idee?

mirage228 29. Jun 2009 08:09

Re: Stringgrid "schnell" beladen
 
Hat das StringGrid eventuell einen virtuellen Modus? Der arbeitet meistens schneller als ein normales Befüllen... Sonst könnte ich natürlich noch das VirtualTreeView empfehlen, mit dem sich auch ganz einfach ein Stringgrid "bauen" lässt...

Viele Grüße

QuickAndDirty 29. Jun 2009 08:27

Re: Stringgrid "schnell" beladen
 
Virtueller Modus ? Hört sich vielversprechend an, aber ich glaube das hat es nicht.

so befülle ich es aktuell:
Delphi-Quellcode:
    oldvisible := visible;
    visible := false;
    j := 1; // Zeile Zahler initialisieren
    RowCount := 2; // Zeilen Anzahl ist 2 ,weniger geht nicht
    Protokoll := Protokoll + 'RowCount := ...init'#13#10;
    DataSet.first;
    While not DataSet.EOF do //bis das ende der datenmenge erreicht ist...
    begin
      try
        For i := 0 to FNames.Count - 1 do // für alle Spalten...
          try
            Cells[Anfang + i, j] := DataSet.Fields[i].asString; //..werte eintragen, Anfang = erste spalte( ?0 / 1?)
          except
            on E:Exception do
              raise EXception.create(e.Message + #13#10
              + 'ThisRow: CELL=' + inttostr(Anfang+i) + ',' + inttostr(j) + #13#10
              + 'Anfang bei=' + inttostr(Anfang) + #13#10
              + 'Recordcount=' + inttostr(Dataset.RecordCount) + #13#10
              + 'Zeilenanzahl=' + inttostr(Rowcount));
          end;
        DataSet.Next;


        if not DataSet.eof then// Wenn noch mehr daten vorhanden..
        begin //..dann schaffe platz
          Inc(j); //erhöhe Zeilen zähler (spalte 0 enthält die Überschriften)
          RowCount := succ(j); // dann Zeilen anzahl mit erhöhen;
        end;


      except
        on E:Exception do
          raise EXception.create(e.Message + 'DS-Loop position='+inttostr(j)+'Zeilenanzahl= '+inttostr(Rowcount));
      end;
    end;
    Visible := oldvisible;  
  except
    on e:Exception do
      raise exception.Create(Self.Name + '.LoadFromDataSet' + #13#10 + Protokoll + #13#10 + e.Message);
  end;
Nachteil ist, dass ein Dialog auf dem das drauf liegt tooootal lange lädt wenn extrem viele Datensätze in das Gitter kommen.
Ich habe mir schon überlegt ob ich das befüllen des Gitters in einem Hintergrund Thread ausführe...aber leider geht das mit Datenbanken multithreaded ja nicht sooo toll...
...ab einer gewissen Anzahl macht so ein Ladebalken schon fast wieder Sinn....

jfheins 29. Jun 2009 08:36

Re: Stringgrid "schnell" beladen
 
Schonmal .BeginUpdate + .EndUpdate ausprobiert?

also vor dem ganzen Zeug .beginupdate aufrufen und danach .endupdate ;)

QuickAndDirty 29. Jun 2009 08:58

Re: Stringgrid "schnell" beladen
 
Ich probiers mal aus

QuickAndDirty 29. Jun 2009 09:02

Re: Stringgrid "schnell" beladen
 
Diese Funktionalität (BeginUpdate, EndUpdate) bietet das Stringgrid nicht.
Aber da ich eh einen Nachfahren habe könnte ich mal sehen ob es möglich ist das dahinterliegende Persistenz Object
vom Typ TStringgridStrings direkt anzusprechen...

DeddyH 29. Jun 2009 09:19

Re: Stringgrid "schnell" beladen
 
Zitat:

Zitat von QuickAndDirty
Diese Funktionalität (BeginUpdate, EndUpdate) bietet das Stringgrid nicht.

Das StringGrid als solches nicht, aber Cols und Rows. Inwiefern Dir das etwas nützt, sei dahingestellt.

Satty67 29. Jun 2009 09:20

Re: Stringgrid "schnell" beladen
 
StringGrid.RowCount für jede einzelne Zeile erhöhen ist sehr langsam. Sieht so aus, als ob Du die Datenmenge nicht weiter filterst, also könntest Du RowCount gleich am Anfang auf den maximalen Wert setzen.

Dann wenn DataSet wärend der Anzeige des Grid die Datenmenge behält, könntest Du selber das Grid nur mit den sichtbaren Zellen "zeichnen". StringGrid.RowCount := 2.000.000 dauert nur ein paar ms und wenn du das Grid nicht wirklich füllst, gibt es keine weitere Verzögerung.

Lannes 29. Jun 2009 09:31

Re: Stringgrid "schnell" beladen
 
Hallo,

man kann RowCount auch erst nach dem Füllen der Zellen auf den endgültigen Wert setzen.

Abgesehen davon ist der Ansatz von Satty67 das schnellste, keine Daten ins Grid setzen, nur die Werte über onDrawCell ausgeben lassen.

hoika 29. Jun 2009 10:07

Re: Stringgrid "schnell" beladen
 
Hallo,

Zitat:

leider dauert Dataset.recordcount auch lange und wird nicht überall unterstützt
Das stimmt schon
select count(*) kennt aber jede (anständige) Datenbank.

Von welcher DB reden wir denn ?

Ich mache das immer so,
alle benötigten Datensätze in eine TList (oder ähnliches) laden
Grid.RowCount:= List.Count+1 (dada ;) ).

Ausserdem weder der alte Spruch:
Wozu 16.000 Records anzeigen ?


Heiko

QuickAndDirty 29. Jun 2009 10:14

Re: Stringgrid "schnell" beladen
 
Zitat:

Zitat von DeddyH
Zitat:

Zitat von QuickAndDirty
Diese Funktionalität (BeginUpdate, EndUpdate) bietet das Stringgrid nicht.

Das StringGrid als solches nicht, aber Cols und Rows. Inwiefern Dir das etwas nützt, sei dahingestellt.

Nützt es?

DeddyH 29. Jun 2009 10:16

Re: Stringgrid "schnell" beladen
 
Nunja, Du könntest ja höchstens zeilenweise BeginUpdate und EndUpdate einsetzen. Das könnte etwas Performance rausholen, aber vermutlich nicht wirklich messbar, es sei denn, Du hast enorm viele Spalten definiert.

QuickAndDirty 29. Jun 2009 10:17

Re: Stringgrid "schnell" beladen
 
Zitat:

Zitat von Satty67
StringGrid.RowCount für jede einzelne Zeile erhöhen ist sehr langsam. Sieht so aus, als ob Du die Datenmenge nicht weiter filterst, also könntest Du RowCount gleich am Anfang auf den maximalen Wert setzen.

Dann wenn DataSet wärend der Anzeige des Grid die Datenmenge behält, könntest Du selber das Grid nur mit den sichtbaren Zellen "zeichnen". StringGrid.RowCount := 2.000.000 dauert nur ein paar ms und wenn du das Grid nicht wirklich füllst, gibt es keine weitere Verzögerung.

Das ist schon mal eine Praktische Sache... Aber Füllen muss ich es wohl, weil ich sonnst das ganzen Inkremtelle suche beim tippen etc. ändern muss. (Das Grid wird als Dropdownlist missbraucht)

QuickAndDirty 29. Jun 2009 10:22

Re: Stringgrid "schnell" beladen
 
Zitat:

Zitat von Lannes
Hallo,
man kann RowCount auch erst nach dem Füllen der Zellen auf den endgültigen Wert setzen.

?? bist du sicher das es dann schneller ist? ich meine er müsste sich ja dann immer im moment des Schreibens speicher hohlen.

Zitat:

Zitat von Lannes
Abgesehen davon ist der Ansatz von Satty67 das schnellste, keine Daten ins Grid setzen, nur die Werte über onDrawCell ausgeben lassen.

:( das geht nicht weil es eine universell Einsetztbare (ist auch überall bei uns eingesetzt) Komponente sein soll...es ist keine Datenbank Komponente.
Und es gibt das Problem "Große Datenmengen flexibel verwalten" dann eben nur wo anders.

matashen 29. Jun 2009 10:23

Re: Stringgrid "schnell" beladen
 
Es wär auch interessant was länger dauert, die Daten holen oder ins Grid schreiben.

wenn das holen länger dauert könntest du das ganze ja in nem extraThread machen um die Anwendung nicht zu blockieren. am besten in ne TList oder so und dann erst am Grid ausgeben

QuickAndDirty 29. Jun 2009 10:29

Re: Stringgrid "schnell" beladen
 
Zitat:

Zitat von hoika
Hallo,

Zitat:

leider dauert Dataset.recordcount auch lange und wird nicht überall unterstützt
Das stimmt schon
select count(*) kennt aber jede (anständige) Datenbank.

Von welcher DB reden wir denn ?

Jede! Es ist keine Datenbankkomponente. Ich biete nur verschiedene Quellen an um es zu füllen...unter anderem auch TDATASET.
( .Loadfrom*() )


Zitat:

Zitat von hoika
Ich mache das immer so,
alle benötigten Datensätze in eine TList (oder ähnliches) laden
Grid.RowCount:= List.Count+1 (dada ;) ).

Das ist ein Scherz oder? Ich meine T.List ist doch auch recht lahm oder?


Zitat:

Zitat von hoika
Ausserdem weder der alte Spruch:
Wozu 16.000 Records anzeigen ?


Heiko

16000 Datensätze in das gitter zu packen ist nicht wirklich sooo Sinnvoll. Es handelt sich hierbei wohl um eine Ausnahme, da hat ein bestimmter Kunde eben soooo viel Datensätze zur Auswahl....und deswegen lahmt der Dialog auf dem die komponente liegt
wenn er erzeugt wird (dauert ca. 7 sekunden, normal ist es garnicht wahrnehmbar) ...ein cachen ist nicht erwünscht...

hoika 29. Jun 2009 10:40

Re: Stringgrid "schnell" beladen
 
Hallo,

kein Scherz.
Warum sollte TList langsam sein ?
Es wird doch intern ein Pointer-Array verwendet.


Heiko

QuickAndDirty 29. Jun 2009 10:46

Re: Stringgrid "schnell" beladen
 
Also hab alles ausprobiert ...keine wahrnehmbare Verbesserung...
:(
Tja, muss ich vielleicht doch mal mit nem DBGrid ähnlichen Ansatz aufwarten....wenn es mal ein paar Kunden
mehr betreffen sollte XD .

Lannes 29. Jun 2009 11:18

Re: Stringgrid "schnell" beladen
 
Hallo,
Zitat:

Zitat von QuickAndDirty
Zitat:

Zitat von Lannes
Hallo,
man kann RowCount auch erst nach dem Füllen der Zellen auf den endgültigen Wert setzen.

?? bist du sicher das es dann schneller ist? ich meine er müsste sich ja dann immer im moment des Schreibens speicher hohlen.

Das schreiben in Cells fordert den Speicher an, setzen von RowCount erweitert nur den Anzeigebereich.
Zeit sparst Du durch das verhindern des mehrmaligen setzen von RowCount, liegt so ca bei 25% Zeitersparnis.
Setzt Du jetzt vor dem Füllen des Grids noch StringGrid.Rows[0].BeginUpdate; und nach dem das Grid gefüllt ist StringGrid.Rows[0].EndUpdate; sparst du nochmal ca. 10%.

Zitat:

Zitat von QuickAndDirty
Zitat:

Zitat von Lannes
Abgesehen davon ist der Ansatz von Satty67 das schnellste, keine Daten ins Grid setzen, nur die Werte über onDrawCell ausgeben lassen.

:( das geht nicht weil es eine universell Einsetztbare (ist auch überall bei uns eingesetzt) Komponente sein soll...es ist keine Datenbank Komponente.
Und es gibt das Problem "Große Datenmengen flexibel verwalten" dann eben nur wo anders.

aber dann ist es wesentlich schneller z.B. mit TList.

Zitat:

Zitat von QuickAndDirty
Zitat:

Zitat von Satty67
StringGrid.RowCount für jede einzelne Zeile erhöhen ist sehr langsam. Sieht so aus, als ob Du die Datenmenge nicht weiter filterst, also könntest Du RowCount gleich am Anfang auf den maximalen Wert setzen.

Dann wenn DataSet wärend der Anzeige des Grid die Datenmenge behält, könntest Du selber das Grid nur mit den sichtbaren Zellen "zeichnen". StringGrid.RowCount := 2.000.000 dauert nur ein paar ms und wenn du das Grid nicht wirklich füllst, gibt es keine weitere Verzögerung.

Das ist schon mal eine Praktische Sache... Aber Füllen muss ich es wohl, weil ich sonnst das ganzen Inkremtelle suche beim tippen etc. ändern muss. (Das Grid wird als Dropdownlist missbraucht)

und gerade da wird es langsam, bzw. deswegen nutze ich immer eine Datenspeicherung außerhalb des Grids und schreib nur den Index der Suchergebnisse in das Grid, der dann in OnDrawCell gelesen, der entsprechende Datensatz(bzw. Feld) aus meiner Datenspeicherung geholt und gezeichnet wird.


Alle Zeitangaben in WEZ +1. Es ist jetzt 14:48 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-2025 by Thomas Breitkreuz