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/)
-   -   TListView - viele Daten - viel Zeit ... (https://www.delphipraxis.net/181102-tlistview-viele-daten-viel-zeit.html)

Marco Steinebach 16. Jul 2014 07:57

TListView - viele Daten - viel Zeit ...
 
Hallo zusammen,
ich habe so ca. 2.000 Datensätze, die angezeigt werden sollen - bis jetzt mittels TListView. Das funktioniert auch alles wunderschön, dauert aber im Aufbau uuunglaublich lange. Ein ernsthafter Geschwindigkeitsgewinn ergibt sich schon, wenn man vor dem Befüllen der Liste sämtliche Spaltenbreiten auf 0 setzt, und sie, nach dem Befüllen, wiederherstellt.
Gibt es eine Komponente, die hierfür geeigneter ist?
Übrigens: D5 Std, also leider nix mit Datenbank o.ä.

Für einen Tipp wäre ich dankbar
Viele Grüße
Marco

mkinzler 16. Jul 2014 07:59

AW: TListView - viele Daten - viel Zeit ...
 
Zitat:

Ein ernsthafter Geschwindigkeitsgewinn ergibt sich schon, wenn man vor dem Befüllen der Liste sämtliche Spaltenbreiten auf 0 setzt, und sie, nach dem Befüllen, wiederherstellt.
Da wäre ein BeginUpdate/EndUpate besser.

Sonst halt den virtuellen Modus verwenden.

p80286 16. Jul 2014 10:22

AW: TListView - viele Daten - viel Zeit ...
 
Zitat:

Zitat von mkinzler (Beitrag 1265632)
Sonst halt den virtuellen Modus verwenden.

Könntest Du da bitte ein paar weitere Informationen zu geben?
die OH kennt nichts "virtuelles".

Gruß
K-H

Sir Rufo 16. Jul 2014 10:29

AW: TListView - viele Daten - viel Zeit ...
 
Zitat:

Zitat von p80286 (Beitrag 1265644)
Zitat:

Zitat von mkinzler (Beitrag 1265632)
Sonst halt den virtuellen Modus verwenden.

Könntest Du da bitte ein paar weitere Informationen zu geben?
die OH kennt nichts "virtuelles".

Gruß
K-H

Die OH in die ich schaue ist da anderer Meinung Delphi-Referenz durchsuchenVcl.ComCtrls.TListView.OwnerData

Mit den angesprochenen Methoden Delphi-Referenz durchsuchenVcl.ComCtrls.TListItems.BeginUpdate und Delphi-Referenz durchsuchenVcl.ComCtrls.TListItems.EndUpdate ist es aber kein Problem selbst 10000 Einträge in so einer ListView in akzeptabler Zeit zu präsentieren.

p80286 16. Jul 2014 12:30

AW: TListView - viele Daten - viel Zeit ...
 
Vielen Dank!
Ich hätte "OwnerData" nie mit "virtuell" in Verbindung gebracht, man lernt nie aus.

Gruß
K-H

Marco Steinebach 16. Jul 2014 14:30

AW: TListView - viele Daten - viel Zeit ...
 
Hallo zusammen,
Tja, wenn's bei euch so schnell geht, dann liegt's evtl. an meinem Screenreader (Programm das den Bildschirminhalt vorliest), das es so lange dauert - probier ich gleich mal aus.
Aaaber: Begin- und EndUpdate hab ich bereits verwendet. Aber, vielleicht bin ich auch heute zu doof, was nützt mir die OwnerData? Ich kann hier zwar Zeiger auf die korrespondierenden Objekte hinterlegen - und jetzt?

Viele Grüße
Marco

Marco Steinebach 16. Jul 2014 14:38

AW: TListView - viele Daten - viel Zeit ...
 
... vielleicht ist ja was anderes falsch...
Delphi-Quellcode:
procedure TPostHauptformular.FuelleEintragsliste;
var
  NeueSpalte: TListColumn;
  NeuerEintrag: TListItem;
  i, x: integer;
  s: string;
  breiten: array of integer;
begin
with liEintraege do // normale TListView, style Report
begin
  {Alle (alten) Spalten und ListenItems bereinigen.}
  columns.clear;
  Items.clear;

  {Memo vorhanden?}
  NeueSpalte := Columns.add;
  NeueSpalte.Caption := ' ';
  NeueSpalte.Width := ColumnTextWidth;

  {Versanddatum}
  NeueSpalte := Columns.add;
  NeueSpalte.Caption := 'Datum';
  NeueSpalte.Width := ColumnTextWidth;
  // und noch 4 stück...

  // breiten speichern und auf 0 setzen...
  SetLength (breiten, Columns.Count);
  for i := 0 to columns.count -1 do
  begin
    breiten[i] := columns[i].Width;
    columns[i].width := 0
  end;
  Items.BeginUpdate;
  for i := 0 to se.letzter do
  begin
  with se[i] do
  begin
    NeuerEintrag := Items.Add;
    neuerEintrag.Caption := s;
    NeuerEintrag.SubItems.Add(empfaenger);
    NeuerEintrag.SubItems.Add(inhalt);
    // und noch'n paar...
  end
  end;
  Items.EndUpdate;
  for i := 0 to columns.count -1 do
    columns[i].Width := breiten[i];

end
end; {FuelleEintragsliste}
ja, ;-), ich weiß, ich hätte alles auf Englisch machen sollen... schlechte Angewohnheit...
Hab ich hier irgendwo einen Grundfehler drin...
Viele Grüße
Marfco

mkinzler 16. Jul 2014 15:00

AW: TListView - viele Daten - viel Zeit ...
 
Zitat:

Aaaber: Begin- und EndUpdate hab ich bereits verwendet.
Jein.
Delphi-Quellcode:
with liEintraege do // normale TListView, style Report
begin
  Items.BeginUpdate;
Zitat:

Ich kann hier zwar Zeiger auf die korrespondierenden Objekte hinterlegen - und jetzt?
Die Erzeugung geht dann schneller. Du musst Dich bei der Anzeige dann selber um die Daten kümmern; dann aber auch nur um die wirklich sichtbaren.

4dk2 16. Jul 2014 15:36

AW: TListView - viele Daten - viel Zeit ...
 
Ne er hat recht, er macht nix falsch ist ein BUG in TCustomListView, der ist sogar noch in XE3 drinne...

das liegt am (ViewStyle = vsReport) der in TCustomListView.ColumnsShowing abgefragt wird.

ist die spaltengröße <= ColumnTextWidth rast er jedesmal ins UpdateColumns,
das macht die Verlangsamung. Müssen nicht mal mehrere Spalten definiert sein.

Also
UpdateColumn
und
UpdateColumns (bei mehreren spalten)
sind die Bösewichte

Delphi-Quellcode:
procedure TListItem.SetCaption(const Value: string);
begin
  if Value <> Caption then
  begin
    FCaption := Value;
    if not Owner.Owner.OwnerData then
{$IFDEF CLR}
      ListView_SetItemText(Handle, Index, 0, IntPtr(Integer(LPSTR_TEXTCALLBACK)));
{$ELSE}
      ListView_SetItemText(Handle, Index, 0, LPSTR_TEXTCALLBACK);
{$ENDIF}
    if ListView.ColumnsShowing and
      (ListView.Columns.Count > 0) and
      (ListView.Column[0].WidthType <= ColumnTextWidth) then
      ListView.UpdateColumns; //hier passierts
    if ListView.SortType in [stBoth, stText] then
      ListView.AlphaSort;
  end;
end;
bei mehreren Spalten ist der Aufrufende
Delphi-Quellcode:
procedure TSubItems.SetColumnWidth(Index: Integer);
var
  ListView: TCustomListView;
begin
  ListView := Owner.ListView;
  if ListView.ColumnsShowing and
    (ListView.Columns.Count > Index) and
    (ListView.Column[Index].WidthType = ColumnTextWidth) then
    ListView.UpdateColumn(Index); //hier....
end;

Ne vernünftige alternative ohne Spaltenbreite auf 0 zu setzen sehe ich leider nicht :(

Marco Steinebach 17. Jul 2014 06:33

AW: TListView - viele Daten - viel Zeit ...
 
Hallo, und herzlichen Dank für eure Antworten.
Prima, und ich dachte schon, ich hätte was grundlegendes nicht verstanden - schon witzig,daß der Bug sogar noch in den neuesten Versionen drin ist...
Bleibt also offensichtlich wirklich nur, spaltenbreite auf 0, oder gleich mit festen Spaltenbreiten zu arbeiten.

Noch eine Frage zu OwnerData:
OwnerData steht auf true. Im einfachsten Beispiel brauche ich dann ja nur im OnData der Listview z.B.
Delphi-Quellcode:
item.caption := format ('Test%d', [item.index]);
zu schreiben, und erhalte, sagen wir der listview.Items.Count steht auf 3, 3 Elemente.
Das funktioniert auch prima. Das OnData wird aber unglaublich oft aufgerufen, egal, ob ich mich durch die Liste bewege, oder nicht. Lasse ich mein Testprogramm, und mehr steht da wirklich nicht drin, einfach 10 Sekunden völlig in Ruhe, hab ich gut 250 Aufrufe von OnData - ähm, hab ich hier was verpaßt, denn ich dachte, das Ereignis wird nur aufgerufen, wenn wirklich was geschrieben werden muß, oder weißt der, ernsthaft, bei jedem Durchlauf, alles neu zu...
Ähm, etwas ratlose Grüße
Marco

mkinzler 17. Jul 2014 07:24

AW: TListView - viele Daten - viel Zeit ...
 
Wenn das Control neu gezeichnet wird. wird der Event pro Item getriggert.

Sir Rufo 17. Jul 2014 07:53

AW: TListView - viele Daten - viel Zeit ...
 
Ich würde zunächst einmal die Methode aufteilen
  1. ListView Columns definieren
    Delphi-Quellcode:
    procedure TPostHauptformular.ListViewColumnsPrepare( ALV : TListView; AColumnTextWidth : Integer );
    var
      LColumn : TListColumn;
    begin
      ALV.Columns.BeginUpdate;
      try
        {Alle (alten) Spalten und ListenItems bereinigen.}
        ALV.Columns.clear;

        {Memo vorhanden?}
        LColumn := ALV.Columns.add;
        LColumn.Caption := ' ';
        LColumn.Width := AColumnTextWidth; // wo kommt die denn her? Mal als Parameter festgelegt s.o.

        {Versanddatum}
        LColumn := ALV.Columns.add;
        LColumn.Caption := 'Datum';
        LColumn.Width := AColumnTextWidth; // wo kommt die denn her? Mal als Parameter festgelegt s.o.

        // und noch 4 stück...

      finally
        ALV.Columns.EndUpdate;
      end;
    end; {FuelleEintragsliste}
  2. ListView Einträge füllen
    Delphi-Quellcode:
    procedure TPostHauptformular.FuelleEintragsliste( ALV : TListView; ADataList : TDataList { <- anpassen } );
    var
      NeuerEintrag: TListItem;
      LDataItem : TDataItem; { <- anpassen }
      i : integer;
      s : string;
    begin
      ALV.Items.BeginUpdate;
      try
        ALV.Items.clear;

        for i := 0 to AList.letzter do
        begin
          LDataItem := ADataList[i];

          NeuerEintrag := ALV.Items.Add;
          NeuerEintrag.Caption := s; // <- den hast du hoffentlich auch gesetzt
          NeuerEintrag.SubItems.Add( LDataItem.empfaenger );
          NeuerEintrag.SubItems.Add( LDataItem.inhalt );
          // und noch'n paar...

        end;
      finally
        Items.EndUpdate;
      end;
    end; {FuelleEintragsliste}
Und diese würde ich auch nicht beide immer ständig aufrufen, sondern nur dann, wenn die benötigt werden. Ist die ListView schon vorbereitet, dann brauche ich die ja nicht noch schon wieder vorbereiten (es ist ja alles da). Und auch wenn man an die Columns geht, dann eben auch dort ein
Delphi-Quellcode:
BeginUpdate ... EndUpdate
und das auch mit einem
Delphi-Quellcode:
try ... finally
absichern.

Wenn du eine generellere Methode zum Vorbereiten einer ListView haben möchtest, dann definierst du dir eben:
Delphi-Quellcode:
TColumnDef = record
  Caption : string;
  Width : Integer;
end;

procedure ListViewPrepare( ALV : TListView; AColumnDefs : array of TColumnDef );
var
  LIdx : Integer;
begin
  ALV.Columns.BeginUpdate;
  try
    ALV.Columns.Clear;
   
    for LIdx := Low(AColumnDefs) to High(AColumnDefs) do
    begin
      LColumn := ALV.Columns.Add;
      LColumn.Caption := AColumnDefs[LIdx].Caption;
      LColumn.Width := AColumnDefs[LIdx].Width;
    end;

  finally
    ALV.Columns.EndUpdate;
  end;
end;
Wie man sieht werden die Methoden wesentlich kürzer aber dafür flexibler :)

kretabiker 17. Jul 2014 08:12

AW: TListView - viele Daten - viel Zeit ...
 
<OT>@Sir Rufo: Schönes Beispiel für ein gelungenes Refactoring: Durch Struktur Klarheit in den Code gebracht und dann noch eine Generalisierung abgeleitet :)</OT>

Sir Rufo 17. Jul 2014 09:34

AW: TListView - viele Daten - viel Zeit ...
 
Nur mal so aus Spaß ... komplett dynamisch :)

Zwei Listen (Personen, Adressen) werden in einer ListView präsentiert. Klick auf Button1 zeigt die Personen, Klick auf Button2 die Adressen.

Das Umschalten zwischen den beiden Listen benötigt bei 9000 Personen ca. 675ms
Der Refresh der Personen-Liste benötigt bei 9000 Personen ca. 330ms

Das Umschalten zwischen den beiden Listen benötigt bei 10000 Adressen ca. 1065ms
Der Refresh der Adressen-Liste benötigt bei 10000 Adressen ca. 740ms

Hier die ganzen Code-Schnipsel (sollte auch mit Delphi 7 so laufen)
Delphi-Quellcode:
unit FormMain;

interface

uses
  {Winapi.} Windows, {Winapi.} Messages,
  {System.} SysUtils, {System.} Variants, {System.} Classes, {System.} Contnrs,
  {Vcl.} Graphics, {Vcl.} Controls, {Vcl.} Forms, {Vcl.} Dialogs, {Vcl.} StdCtrls, {Vcl.} ComCtrls,

  DataListContainer;

type
  TForm1 = class( TForm )
    ListView1 : TListView;
    Button1 : TButton;
    Button2 : TButton;
    Label1 : TLabel;
    procedure Button1Click( Sender : TObject );
    procedure Button2Click( Sender : TObject );
  private
    FPersons : TObjectList;
    FAddresses : TObjectList;
    FPersonsPresenter : TDataListContainer;
    FAddressPresenter : TDataListContainer;
    procedure ShowInfo( AStart, AStop : TDateTime; ACount : Integer );
  public
    procedure AfterConstruction; override;
    procedure BeforeDestruction; override;
  end;

var
  Form1 : TForm1;

implementation

{$R *.dfm}

uses
  {System.} DateUtils,
  DataListToListView,
  Person, Address;

{ TForm1 }

procedure TForm1.AfterConstruction;
var
  LIdx : Integer;
begin
  inherited;
  // Musterdaten erstellen
  FPersons := TObjectList.Create( True );
  for LIdx := 1 to 3000 do
    begin
      FPersons.Add( TPerson.Create( 'Lustig, Peter', EncodeDate( 1975, 1, 1 ) ) );
      FPersons.Add( TPerson.Create( 'Traurig, Walter', EncodeDate( 1975, 2, 1 ) ) );
      FPersons.Add( TPerson.Create( 'Mustermann, Erika', EncodeDate( 1975, 3, 1 ) ) );
    end;
 
  // Definition der Spalten
  FPersonsPresenter := TDataListContainer.Create;
  FPersonsPresenter.AddColumn( 'Name', 'Fullname', 150 );
  FPersonsPresenter.AddColumn( 'Geburtstag', 'DOB', 80 );
  FPersonsPresenter.DataList := FPersons;

  // Musterdaten erstellen
  FAddresses := TObjectList.Create( True );
  for LIdx := 1 to 2500 do
    begin
      FAddresses.Add( TAddress.Create( 'Am Walde 23', 12345, 'Hinterm Berg' ) );
      FAddresses.Add( TAddress.Create( 'Im Weiher 12', 23456, 'Vordem Berg' ) );
      FAddresses.Add( TAddress.Create( 'Auf der Hecke 5', 34567, 'Beidem Berg' ) );
      FAddresses.Add( TAddress.Create( 'Nebenstollen 5', 45678, 'Unterm Berg' ) );
    end;

  // Definition der Spalten
  FAddressPresenter := TDataListContainer.Create;
  FAddressPresenter.AddColumn( 'Straße', 'Street', 150 );
  FAddressPresenter.AddColumn( 'PLZ', 'ZipCode', 80 );
  FAddressPresenter.AddColumn( 'Ort', 'City', 80 );
  FAddressPresenter.DataList := FAddresses;
end;

procedure TForm1.BeforeDestruction;
begin
  inherited;
  FPersonsPresenter.Free;
  FPersons.Free;
  FAddressPresenter.Free;
  FAddresses.Free;
end;

procedure TForm1.Button1Click( Sender : TObject );
var
  LStart, LStop : TDateTime;
begin
  LStart := Now;
  PresentData( ListView1, FPersonsPresenter );
  LStop := Now;
  ShowInfo( LStart, LStop, FPersonsPresenter.DataList.Count );
end;

procedure TForm1.Button2Click( Sender : TObject );
var
  LStart, LStop : TDateTime;
begin
  LStart := Now;
  PresentData( ListView1, FAddressPresenter );
  LStop := Now;
  ShowInfo( LStart, LStop, FAddressPresenter.DataList.Count );
end;

procedure TForm1.ShowInfo( AStart, AStop : TDateTime; ACount : Integer );
begin
  Label1.Caption := Format( '%d Einträge in %dms', [ACount, MilliSecondsBetween( AStop, AStart )] );
end;

end.
Delphi-Quellcode:
unit Person;

interface

type
  TPerson = class
  private
    FFullname : string;
    FDOB : TDate;
  public
    constructor Create( const Fullname : string; DOB : TDate );
  published
    property Fullname : string read FFullname write FFullname;
    property DOB : TDate read FDOB write FDOB;
  end;

implementation

{ TPerson }

constructor TPerson.Create( const Fullname : string; DOB : TDate );
begin
  inherited Create;
  FFullname := Fullname;
  FDOB := DOB;
end;

end.
Delphi-Quellcode:
unit Address;

interface

type
  TAddress = class
  private
    FStreet : string;
    FZipCode : Integer;
    FCity : string;
  public
    constructor Create( const Street : string; ZipCode : Integer; const City : string );
  published
    property Street : string read FStreet write FStreet;
    property ZipCode : Integer read FZipCode write FZipCode;
    property City : string read FCity write FCity;
  end;

implementation

{ TAddress }

constructor TAddress.Create( const Street : string; ZipCode : Integer; const City : string );
begin
  inherited Create;
  FStreet := Street;
  FZipCode := ZipCode;
  FCity := City;
end;

end.
Delphi-Quellcode:
unit DataListContainer;

interface

uses
  Contnrs;

type
  TDataColumnDef = record
    Caption : string;
    PropertyName : string;
    Width : Integer;
    Visible : Boolean;
  end;

  TDataColumnDefs = array of TDataColumnDef;

  TDataListContainer = class
  private
    FDataList : TObjectList;
    FColumnDefs : TDataColumnDefs;
  public
    procedure AddColumn( ACaption, APropertyName : string; AWidth : Integer; AVisible : Boolean = true );

    property ColumnDefs : TDataColumnDefs read FColumnDefs;
    property DataList : TObjectList read FDataList write FDataList;
  end;

implementation

{ TDataListContainer }

procedure TDataListContainer.AddColumn( ACaption, APropertyName : string; AWidth : Integer; AVisible : Boolean );
var
  LIdx : Integer;
begin
  LIdx := Length( FColumnDefs );
  SetLength( FColumnDefs, LIdx + 1 );
  FColumnDefs[LIdx].Caption := ACaption;
  FColumnDefs[LIdx].PropertyName := APropertyName;
  FColumnDefs[LIdx].Width := AWidth;
  FColumnDefs[LIdx].Visible := AVisible;
end;

end.
Delphi-Quellcode:
unit DataListToListView;

interface

uses
  {Vcl.} ComCtrls,
  DataListContainer;

procedure PresentData( AListView : TListView; AContainer : TDataListContainer );
procedure PrepareColumns( AListView : TListView; AContainer : TDataListContainer );
procedure FillData( AListView : TListView; AContainer : TDataListContainer );

implementation

uses
  {System.} TypInfo;

procedure PresentData( AListView : TListView; AContainer : TDataListContainer );
begin
  PrepareColumns( AListView, AContainer );
  FillData( AListView, AContainer );
end;

procedure PrepareColumns( AListView : TListView; AContainer : TDataListContainer );
var
  LCount : Integer;
  LIdx : Integer;
  LColumn : TListColumn;
begin
  AListView.Columns.BeginUpdate;
  try

    LCount := Length( AContainer.ColumnDefs );

    // Spalten hinzufügen, wenn nicht ausreichend vorhanden
    while AListView.Columns.Count < LCount do
      AListView.Columns.Add;

    for LIdx := 0 to AListView.Columns.Count - 1 do
      begin
        LColumn := AListView.Columns.Items[LIdx];
        if LIdx < LCount
        then
          begin
            LColumn.Caption := AContainer.ColumnDefs[LIdx].Caption;
            if AContainer.ColumnDefs[LIdx].Visible
            then
              LColumn.Width := AContainer.ColumnDefs[LIdx].Width
            else
              LColumn.Width := 0;
          end
        else
          begin
            LColumn.Caption := '';
            LColumn.Width := 0;
          end;
      end;

  finally
    AListView.Columns.EndUpdate;
  end;
end;

procedure FillData( AListView : TListView; AContainer : TDataListContainer );
var
  LIdx : Integer;
  LCount : Integer;
  LItem : TListItem;
  LDataItem : TObject;
  LColIdx : Integer;
  LColCount : Integer;
  LPropValue : string;
begin
  AListView.Items.BeginUpdate;
  try

    if Assigned( AContainer.DataList )
    then
      LCount := AContainer.DataList.Count
    else
      LCount := 0;

    if AListView.Items.Count - LCount > LCount
    then
      AListView.Items.Clear;

    // Zeilen hinzufügen, wenn nicht ausreichend vorhanden
    while AListView.Items.Count < LCount do
      AListView.Items.Add;

    // Zeilen entfernen, wenn zuviel
    while AListView.Items.Count > LCount do
      // Löschen immer vom Ende her, das spart Zeit
      AListView.Items.Delete( AListView.Items.Count - 1 );

    for LIdx := 0 to LCount - 1 do
      begin
        LItem := AListView.Items[LIdx];
        LDataItem := AContainer.DataList.Items[LIdx];
        LColCount := Length( AContainer.ColumnDefs );

        // SubItems
        LItem.SubItems.BeginUpdate;
        try
          LItem.SubItems.Clear;

          for LColIdx := 0 to LColCount - 1 do
            begin
              LPropValue := GetPropValue( LDataItem, AContainer.ColumnDefs[LColIdx].PropertyName, True );

              if LColIdx = 0
              then
                // Caption
                LItem.Caption := LPropValue
              else
                LItem.SubItems.Add( LPropValue );

            end;

        finally
          LItem.SubItems.EndUpdate;
        end;
      end;

  finally
    AListView.Items.EndUpdate;
  end;
end;

end.

Marco Steinebach 17. Jul 2014 13:14

AW: TListView - viele Daten - viel Zeit ...
 
Hallo,
joa, das ist doch mal ein Beispiel! wow!!
Mein Code macht, dem Grunde nach, das Gleiche - ich hab natürlich das try-except nicht drin - und das hier kannstde natürlich allgemeiner verwenden.

Meine Prozedur zum Füllen der Liste wird nur einmal aufgerufen, oder per Hand, zum Aktualsisieren der Ansicht.
Und da das Befüllen mit ca. 530 Zeilen knapp 800 ms dauert, wollte ich's ein bißchen schneller haben.
Deshalb kam ich ja auch auf die Idee, OwnerData auf true zu setzen, ich hänge aber immernoch daran, daß er OnData so oft aufruft, selbst wenn ich das Programm komplett in Ruhe lasse, also, denke ich, nix neu gezeichnet werden muß.
Vielleicht hat ja doch noch jemand einen Tipp, sonst muß ich eben mit den 800 ms leben.
Was ich, fällt mir gerade ein, noch nicht ausprobiert habe ist, die Zeilen zunächst anzulegen, und dann indiziert zu füllen - wie in deinem Beispiel - wer weiß, ob das nochwas bringt...

Viele Grüße
Marco

Sir Rufo 17. Jul 2014 13:42

AW: TListView - viele Daten - viel Zeit ...
 
Schau dir deinen Code nochmal genauer an.

Du rufst
Delphi-Quellcode:
Items.Clear
auf, obwohl du noch nicht
Delphi-Quellcode:
Items.BeginUpdate
aufgerufen hast.

Mach das und du wirst sehen, dass es schneller geht.

Das Wiederverwenden der ListItems spart auch noch eine Menge Zeit ein.

jaenicke 17. Jul 2014 14:24

AW: TListView - viele Daten - viel Zeit ...
 
Auch hier poste ich einmal den Hinweis auf die VirtualTreeView:
http://www.jam-software.com/virtual-treeview/
Darin haben wir durchaus auch hunderttausende Einträge, die innerhalb weniger Millisekunden angezeigt werden. Oder z.B. 10000 Einträge, die absolut live bei der Eingabe eines Filters gefiltert werden.

Ich probiere heute Abend einmal das Beispiel von Sir Rufo darin aus.

p80286 17. Jul 2014 21:03

AW: TListView - viele Daten - viel Zeit ...
 
Könnte man den Thread nach Tutorial oder die CodeLibrary verschieben?

Gruß
K-H

jaenicke 17. Jul 2014 21:36

AW: TListView - viele Daten - viel Zeit ...
 
Liste der Anhänge anzeigen (Anzahl: 1)
So, ich habe es nur ganz kurz gemacht, eigentlich könnte man das noch deutlich schöner machen. Ergebnisse jedenfalls:
Das Umschalten zwischen den beiden Listen benötigt bei 9000 Personen ca. 2ms
Der Refresh der Personen-Liste benötigt bei 9000 Personen ca. 2ms

Das Umschalten zwischen den beiden Listen benötigt bei 10000 Adressen ca. 2ms
Der Refresh der Adressen-Liste benötigt bei 10000 Adressen ca. 2ms

Das Projekt liegt im Anhang.

Füge ich die Daten direkt in die Knoten als Daten ein, dauern die Operationen ca. 10ms. Sprich anders als im Anhang:
Delphi-Quellcode:
procedure FillData(AListView: TVirtualStringTree; AContainer: TDataListContainer);
var
  i: Integer;
begin
  AListView.BeginUpdate;
  try
    AListView.Clear;
    if Assigned(AContainer.DataList) then
      for i := 0 to AContainer.DataList.Count - 1 do
        AListView.AddChild(nil, AContainer.DataList[i]);
  finally
    AListView.EndUpdate;
  end;
end;
Und:
Delphi-Quellcode:
procedure TForm6.VirtualStringTree1GetText(Sender: TBaseVirtualTree;
  Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType;
  var CellText: string);
var
  CurrentEntry: TObject;
begin
  CurrentEntry := TObject(Sender.GetNodeData(Node)^);
  if CurrentEntry is TPerson then
...

Marco Steinebach 18. Jul 2014 07:42

AW: TListView - viele Daten - viel Zeit ...
 
Zitat:

Zitat von Sir Rufo (Beitrag 1265786)
...Du rufst
Delphi-Quellcode:
Items.Clear
auf, obwohl du noch nicht
Delphi-Quellcode:
Items.BeginUpdate
aufgerufen hast.

Mach das und du wirst sehen, dass es schneller geht.

jawoll, und wie ich das sehe! ;-) Dauert nur noch knapp die Hälfte an Zeit.
Meine Güte, manchmal programmiert man sich einen Mist zusammen, wie kann man denn erst Clear und dann BeginUpdate aufrufen ...
Vielen Dank!

Zitat:

Zitat von Sir Rufo (Beitrag 1265786)
Das Wiederverwenden der ListItems spart auch noch eine Menge Zeit.

Das kommt als nächstes - mal sehen wieviel es bringt...

Ich schau mir jetzt mal VirtualTreeview an - wenn das Ergebnis mit Screenreader gut auszulesen ist - warum nicht wechseln...

So hat sich auch, schlicht weil's nicht nötig ist, das Problem mit dem OwnerData gelöst ;-).

Viele Grüße und ganz herzlichen Dank!
Marco

jaenicke 18. Jul 2014 10:17

AW: TListView - viele Daten - viel Zeit ...
 
Zitat:

Zitat von Marco Steinebach (Beitrag 1265860)
Ich schau mir jetzt mal VirtualTreeview an - wenn das Ergebnis mit Screenreader gut auszulesen ist - warum nicht wechseln...

Ich weiß nicht, ob man das noch aktivieren muss, aber VTAccessibilityFactory als Unit existiert z.B., insofern gehe ich mal davon aus, dass auch Screenreader gehen könnten. Ausprobiert habe ich es nicht.

ManfredG 14. Okt 2018 12:33

AW: TListView - viele Daten - viel Zeit ...
 
Hallo Marco,
ich habe ebenfalls festgestellt, daß OnData sehr oft aufgerufen wird.
Hast du herausbekommen, warum?

Gruß Manfred

DieDolly 14. Okt 2018 14:45

AW: TListView - viele Daten - viel Zeit ...
 
Manfred, vernachlässige TListView einfach und sattel über zu VirtualTreeview.
Erspar dir die Kopfschmerzen die dir TListView bringen werden.

dummzeuch 14. Okt 2018 16:03

AW: TListView - viele Daten - viel Zeit ...
 
Der Thread ist zwar schon "etwas" älter, aber: Ein TStringGrid ist deutlich schneller als ein TListView im Report-Modus und ein TCustomGrid mit eigenen Events (z.B. tdzVirtualStringGrid aus meiner dzLib) ist nochmal um Klassen schneller. Theming kann man dann allerdings größtenteils vergessen.


Alle Zeitangaben in WEZ +1. Es ist jetzt 02:18 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz