TListView schnell laden



  • Hallo,

    vielleicht kann mir jmd von euch helfen. Ich hab ein Tool geschrieben, mit dem ich Logfiles auslese und die Einträge aus dem Logfile in einen ListView lade. Mein ListView hat 8 Columns.

    Nun habe ich das Problem, dass bei sehr großen LogFiles (über 30.000 Einträge), das addieren der Einträge in den ListView über 2 Minuten dauert. Die Zeit für das Auslesen der LogFiles ist hierbei schon abgezogen. Hab es auch schon mit BeginUpdate/EndUpdate probiert. Brachte aber höchstens ein paar Sekündchen.

    Meine Frage wäre nun, ob es noch andere Möglichkeiten gibt das Hinzufügen von Items und SubItems in einem ListView zu beschleunigen. Hab ihr irgendwelche Ideen?

    Gruß



  • Zeig doch mal den entsprechenden Code-Abschnitt und ggf. das Dateiformat.

    Ansonsten sieh dir mal das VirtualListView-Beispiel unter \examples an.



  • Hi,

    hier mal der Code:

    //ListView füllen
    for(int i = 0; i < iAnzahl-1; ++i)
    {
       if(strcmp(logEntries[i].Date.c_str(), "\0") == 0)
       {
          if(i != 0 && ListView->Items->Count != 0)
          {
             AnsiString sTemp = ListView->Items->Item[ListView->Items->Count-1]->SubItems->Strings[6];
                   sTemp += logEntries[i].Message.c_str();
                   sTemp += ";";
                   ListView->Items->Item[ListView->Items->Count-1]->SubItems->Strings[6] = sTemp;
                }
             }
             else
             {
                TListItem *item = ListView->Items->Add();
                item->Caption = logEntries[i].Date.c_str();
                item->SubItems->Add(logEntries[i].Time.c_str());
                item->SubItems->Add(logEntries[i].Modul.c_str());
                item->SubItems->Add(logEntries[i].Line.c_str());
                item->SubItems->Add(logEntries[i].Thread.c_str());
                item->SubItems->Add(logEntries[i].Process.c_str());
                item->SubItems->Add(logEntries[i].Severity.c_str());
                item->SubItems->Add(logEntries[i].Message.c_str());
             }
    
             LoadForm->stepProgressBar();
    
             LogEntry listEntry;
             listEntry.Date = logEntries[i].Date;
             listEntry.Time = logEntries[i].Time;
             listEntry.Modul = logEntries[i].Modul;
             listEntry.Line = logEntries[i].Line;
             listEntry.Thread = logEntries[i].Thread;
             listEntry.Process = logEntries[i].Process;
             listEntry.Severity = logEntries[i].Severity;
             listEntry.Message = logEntries[i].Message;
    
             this->m_lLogEntry.push_back(listEntry);
    
             if(i%100 == 0)
                ListView->Repaint();
          }
    


  • Sorry, bin irgendwie auf ne Taste gekommen, dass der den Beitrag schon postet.

    Das logentries[] ist ne Liste in die ich das File schon eingelsen hab. Das mach ich mit der Boost Library. Aber das dauert nicht lang. Die meiste Zeit nimmt der gepostete Code in Anspruch. Hab das schon gemessen.

    Werd mir das Beispiel mal anschauen!!!

    Gruß



  • Du solltest vor dem Bearbeiten des ListViews

    ListView->Items->BeginUpdate()
    

    schreiben, und am Ende

    ListView->Items->EndUpdate()
    

    dadurch wird der Listview nicht bei jeder Änderung neu gezeichnet, sondern erst, wenn du fertig bist mit Ändern.

    mfg
    xXx



  • Das hat er ja schon gemacht.

    @_gast_
    Verwende bitte die Code-Tags. So ist das etwas schlecht zu lesen.
    Ich nehmen mal an, du verwendest std::string. Hiermit willst du wohl testen ob der String leer ist

    if(strcmp(logEntries[i].Date.c_str(), "\0") == 0)
    

    einfacher so

    if(logEntries[i].Date.empty())
    


  • Hi,

    danke für die Hinweise, aber das mit dem BeginUpdate() und EndUpdate() hab ich schon drin. Und das mit der empty-Abfrage hab ich eingebaut und es bringt nicht viel zwei, drei Sekündchen.

    Hat vielleicht jmd von euch noch noch Beispiel für einen virtual ListView, ausser dem das bei C++ Builder in den Examples steht?

    Gruß



  • Wofür ist denn das Repaint am Ende der Schleife (bei 30000 Einträgen werden dann ja 300 Repaint-Aufrufe gemacht)?

    P.S. Wie zeitraubend ist denn die Methode "LoadForm->stepProgressBar()", da die jedesmal aufgerufen wird?

    Ich selber hatte auch schon das Problem mit ListView und großen Daten.
    Ich habe dann das Hinzufügen der Daten in einen eigenen Thread ausgelagert, so daß der Anwender in der Zwischenzeit schon mal weiterarbeiten kann, während die ListView gefüllt wird.



  • Ich habe in solchen Fällen einfach LockWindowUpdate() verwendet. Kann natürlich
    sein, dass BeginUpdate()/EndUpdate() was ähnliches bewirkt.

    Gruß

    Alexander



  • Will man viele Daten in einer ListView anzeigen, kann man die mit dem Ereignis "OnData" machen (Wie im Borland-Beispiel VirtualListView).

    Hier ein kleines Beispiel:

    #include <vcl.h>
    #pragma hdrstop
    #include "Unit1.h"
    #pragma package(smart_init)
    #pragma resource "*.dfm"
    
    #define ANZAHLDATEN 30000
    
    TForm1 *Form1;
    TList *MeineDaten;       
    
    struct Daten
    {
      String vorname;
      String nachname;
    
      Daten(String Vorname, String Nachname)
      {
       vorname = Vorname;
       nachname = Nachname;
      }
    };
    //---------------------------------------------------------------------------
    String ZufallsString()
    {
     int length = random(10) + 3;
     String zstring = "";
     char chars [] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
     for (int i=0;i<length;i++)
     {
      zstring += chars[ random( strlen(chars) ) ];
     }
     return zstring;
    }
    //---------------------------------------------------------------------------
    __fastcall TForm1::TForm1(TComponent* Owner)        : TForm(Owner){}
    //---------------------------------------------------------------------------
    void __fastcall TForm1::FormCreate(TObject *Sender)
    {
     randomize();
     //Hier sammeln wir unsere Daten drin
     MeineDaten = new TList();
     //OwnerData auf true setzen, sonst geht das Beispiel nicht
     ListView1->OwnerData = true;
     ListView1->RowSelect = true;
     ListView1->ViewStyle = vsReport;
     //Zwei Spalten einfügen
     TListColumn *column = ListView1->Columns->Add();
     column->Caption = "Index";
     column = ListView1->Columns->Add();
     column->Caption = "Spalte1";
     column = ListView1->Columns->Add();
     column->Caption = "Spalte2";
     //Dummydaten erzeugen
     for(int i=0;i<ANZAHLDATEN;i++)
     {
       MeineDaten->Add( new Daten( ZufallsString() , ZufallsString() ) );
     }
    
     //ListView-Inhalt anzeigen
     FuelleView();
    
    }
    //---------------------------------------------------------------------------
    void __fastcall TForm1::ListView1Data(TObject *Sender, TListItem *Item)
    {
     if ((Item->Index < MeineDaten->Count))
     {
      Daten *d =(Daten*)MeineDaten->Items[Item->Index];
      Item->Caption = IntToStr(Item->Index);
      Item->SubItems->Add(d->vorname);
      Item->SubItems->Add(d->nachname);
     }
    }
    //---------------------------------------------------------------------------
    void TForm1::FuelleView(void)
    {
      ListView1->Items->Count = MeineDaten->Count;
      ListView1->Items->BeginUpdate();
      try
      {
       if (ListView1->Items->Count > 0)
       {
        //Erste Element markieren
        ListView1->Selected = ListView1->Items->Item[0];
        ListView1->Selected->Focused = true;
        ListView1->Selected->MakeVisible(false);
       }
      }
      __finally
      {
       ListView1->Items->EndUpdate();
      }
    }
    

    MfG Barracuda



  • @Barracuda: coole Sache funktioniert echt flott 😃 Danke dir!

    Habe meine code gleich mal darauf abgestimmt und siehe da 10.000 entries 2 Sek.

    😕 Hänge hier jedoch noch an funktionen die direkt auf ein Item eines TListView zugreifen und z.B. den ImageIndex des Items ändern wollen ?? Könnte jemand dem obigen Beispiel noch ein OnButtonClick hinzufügen was "direkt" ein Item verändert.

    Danke, und frohe feier tage (ferien) ... :xmas1:



  • Ich grabe den Thread nochmal aus...

    Habe das jetzt so implementiert, aber irgendwie klappts bei mir noch nicht. Ich habe mit dem Debugger mal in das "OnData"-Event reingeschaut und festgestellt, dass sich Item->Index nie ändert und immer auf 0 bleibt.

    Hier ist mal mein Code:

    void TMainWindow::showSelectedModule()
    {
      string module = lv_moduleList->Selected->Caption.c_str();//lv_moduleList enthält eine Liste mit Dateipfaden
      functionList.initModulefuncList(module); //Liste wird nach Modulnamen sortiert; modulfuncVector wird erstellt
    
    //Ab hier wird wichtig ;-)
    
      m_MeineDaten = new TList();
    
      for(int n = 0; n < functionList.getModulefunclistSize(); n++) //functionList enthält einen Vektor der Objekte vom Typ TModuleFunction enthält
      {
      	m_MeineDaten->Add(&functionList.getModulefunctionAt(n));
      }
    
      lv_moduleView->Items->Count = m_MeineDaten->Count;
         lv_moduleView->Items->BeginUpdate();
         try
         {
           if (lv_moduleView->Items->Count > 0)
           {
             lv_moduleView->Selected = lv_moduleView->Items->Item[0];
             lv_moduleView->Selected->Focused = true;
             lv_moduleView->Selected->MakeVisible(false);
           }
         }
      __finally
      {
         lv_moduleView->Items->EndUpdate();
      }
    }
    
    void __fastcall TMainWindow::lv_moduleViewData(TObject *Sender,
          TListItem *Item)
    {
      if ((Item->Index < m_MeineDaten->Count))
         {
           TModuleFunction *tempfunc =(TModuleFunction*)m_MeineDaten->Items[Item->Index];
           Item->Caption = tempfunc->getTimestamp().c_str();
           Item->SubItems->Add(tempfunc->getState().c_str());
           Item->SubItems->Add(tempfunc->getFuncname().c_str());
           Item->SubItems->Add(tempfunc->getDuration().c_str());
         }
    }
    

    An sich haargenau das gleiche wie Barracuda gepostet hat, bloß an mein Programm angepasst oder?

    Also die Werte die in "tempfunc" stehen sind immer die gleichen.

    Oder liegt das daran dass man bei solchen Events schwer mit Debugger arbeiten kann und er mir deshalb immer die gleichen Werte anzeigt?

    edit: Habs gelöst. Da ich ja schon einen Vektor mit Daten habe brauchste ioch die TList ja gar nicht 😉 Also alles was mit T-List zu tun hatte rausgeschmissen und durch ven Vektor ersetzt und siehe da...läuft ^^


Anmelden zum Antworten