Einzelnen Beitrag anzeigen

Benutzerbild von Sir Rufo
Sir Rufo

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

AW: SQL-Abfrage im Thread und füllen eines ListView

  Alt 28. Sep 2011, 23:11
Ok, hier mal ein Thread, der Datensätze an eine TListView schickt.

Der passt da noch nicht ganz für Dich (kein ZEOS, keine Query) aber das Prinzip bleibt gleich.

Daten lesen und dann immer blockweise an die ListView schicken.
so lange wie der Thread noch läuft per Queue und der letzte Abgleich erfolgt per Synchronize.
(Damit wird sichergestellt, dass alle Einträge auch die ListView ausgeliefert werden)

Delphi-Quellcode:
unit thread.SqlDataToListView;

interface

uses
  Classes,
  ComCtrls, // TListView
  DB, DBClient, // TClientDataSet
  Generics.Collections; // TList<T>

type
  TSqlDataToListViewThread = class( TThread )
  private
    FListView : TListView;
    FData : TDataSet;
  protected
    procedure SendDataToListView( AListView : TListView; AItems : TList<TStrings>; Sync : Boolean = False );
  protected
    procedure Execute; override;

  public
    constructor Create( AListView : TListView; const AFileName : string; CreateSuspended : Boolean = False );
    destructor Destroy; override;
  end;

implementation

uses
  Windows, SysUtils;

{ TGetSqlDataToListViewThread }

constructor TSqlDataToListViewThread.Create( AListView : TListView; const AFileName : string; CreateSuspended : Boolean );
begin
  inherited Create( CreateSuspended );

  FListView := AListView;
  FData := TClientDataSet.Create( nil );

  with FData as TClientDataSet do
    begin
      FileName := AFileName;
    end;
end;

destructor TSqlDataToListViewThread.Destroy;
begin
  FData.Free;
  inherited;
end;

procedure TSqlDataToListViewThread.Execute;
var
  lItems : TList<TStrings>;
  lItem : TStrings;
  lField : TField;

begin

  // Datenverbindung öffnen

  try
    FData.Open;
  except
    on E : Exception do
  end;

  if FData.Active
  then

    // Wenn die Datenverbindung gesichert hergestellt ist dann können wir ja ans Werk

    try

      lItems := TList<TStrings>.Create;
      try

        while not Terminated and not FData.Eof do

          // Wir machen hier so lange, bis ...
          // ... der Thread abgebrochen wird
          // ... oder alle Datensätze gelesen wurden

          begin

            // Daten in einen Puffer schieben

            lItem := TStringList.Create;
            for lField in FData.Fields do
              begin
                lItem.Add( lField.AsString );
              end;

            lItem.Add( DateTimeToStr( now ) );

            // Daten in die Sammelliste schreiben

            lItems.Add( lItem );

            // Nächster Datensatz
            FData.Next;

            // Wir tun mal so, als ob das hier gaaaanz lange dauert
            Sleep( Random( 15 ) );


            if ( lItems.Count >= 10 ) or Terminated or FData.Eof
            then

              // Wenn der Block voll ist,
              // oder der Thread abgebrochen wurde
              // oder keine Daten mehr zu lesen sind
              // dann die Daten an das ListView ausliefern

              SendDataToListView( FListView, lItems, Terminated or FData.Eof );

          end;

      finally
        lItems.Free;
      end;

    finally
      FData.Close;
    end;
end;

procedure TSqlDataToListViewThread.SendDataToListView( AListView : TListView; AItems : TList<TStrings>; Sync : Boolean );
var
  lItems : TObjectList<TStrings>; // Mal hier schnell geändert, sonst haben wir da ein Speicherleck :o)
  lItem : TStrings;
begin
  if MainThreadID = GetCurrentThreadId
  then

    // Ei jo, wenn wir uns jetzt im MainThread-Kontext befinden,
    // dann können wir ja wieder ganz gemütlich auf das VCL-Gedöns zugreifen

    begin

      if Assigned( AItems )
      then
        begin

          AListView.Items.BeginUpdate;
          try

            for lItem in AItems do
              begin
                with AListView.Items.Add do
                  begin
                    Caption := lItem[0];
                    lItem.Delete( 0 );
                    SubItems.Assign( lItem );
                    SubItems.Add( DateTimeToStr( now ) );
                    SubItems.Add( BoolToStr( Sync, True ) );
                  end;
              end;

          finally
            AListView.Items.EndUpdate;
          end;

          AItems.Free; // ** Hier ist das Free, und ...
        end;

    end
  else
    begin

      // Kopieren der übergebenen Daten-Liste
      lItems := TObjectList<TStrings>.Create; // ** ... hier das Create ... verkehrte Welt :o)
      for lItem in AItems do
        begin
          lItems.Add( lItem );
        end;

      // übergebene Daten-Liste leeren (da schreibt der Thread ja wieder neue Daten rein)
      AItems.Clear;

      // Jetzt rufen wir uns selber nochmal auf, aber ...
      // 1. mit der kopierten Liste
      // 2. im MainThread-Kontext (Synchronized oder Gequeued)

      if Sync
      then
        Synchronize( procedure begin SendDataToListView( AListView, lItems, Sync ); end )
      else
        Queue( procedure begin SendDataToListView( AListView, lItems, Sync ); end );
    end;
end;

end.
Im Anhang ScreenShot, Exe-Datei und der gesamte Source
Miniaturansicht angehängter Grafiken
threadgetsqldata_s1.png  
Angehängte Dateien
Dateityp: zip ThreadGetSQLData.zip (834,1 KB, 37x aufgerufen)
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 (28. Sep 2011 um 23:25 Uhr)
  Mit Zitat antworten Zitat