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.