| E | L | P | H | I | R | O    programmeren •  linux •  gitaar •  foto's •  email
Search with Google™:
    sdl •  sql •  delphi •  componenten •  cpp •  c


Dynamische lijsten

De TList klasse is een hele sterke (en veel gebruikte) klasse om een dynamische lijst van pointers in op te slaan. Je kunt deze lijst vrij eenvoudig uitbreiden om hem voor een specifiek type datastructuur geschikt te maken.

Stel je hebt de volgende code met een record waarin je de volgende informatie opslaat:

PInfoType = ^TInfoType; //pointer naar een recordtype
TInfoType = record
 iNummer: integer;
 sNaam: string;
end;


Je kunt nu eenvoudig een lijst maken met alle functionaliteit van de TList (toevoegen, verwijderen, invoegen etc) door de volgende aanpassingen op de procedures Get, Put, Notify en Add en de property Items van de TList klasse uit te voeren:

TInfoList = class( TList )
 function Get(index: integer ): PInfoType;
 procedure Put( index: integer; pElement: PInfoType );
 procedure Notify( ptr: pointer; eAction: TListNotification ); override;
public
 procedure Add( pElement: PInfoType );
 property Items[ index: integer]: PInfoType read Get write Put; default;
end;


Waarbij de implementatie er zo uit ziet:

function TInfoList.Get( index: integer ): PInfoType;
begin
 result := PInfoType( inherited Get( index )); //typecast
end;

procedure TInfoList.Put( index: integer; pElement: PInfoType );
begin
 inherited Put( index, pElement );
end;

procedure TInfoList.Notify(ptr: pointer; eAction: TListNotification);
begin
 if eAction = lnDeleted then
  Dispose( ptr );
end;

procedure TInfoList.Add( pElement: PInfoType );
begin
 inherited Add( pElement );
end;


Hiermee heb je genoeg code om een flexibele lijst te creëren zoals in de volgende voorbeeldcode wordt gedaan;

Ergens in de code moet een variabele van het type TInfoList aangegeven zijn;
lstGegevens: TInfoList
en ergens in de code moet deze variabele aangemaakt worden;
lstGegevens := TInfoList.Create
Nu kan bijvoorbeeld een volgende procedure geschreven worden om informatie aan de lijst toe te voegen;

procedure VoegWatToe;
var ptr: PPrevMonthInfo;
begin
 New( ptr );
 ptr^.iNummer := 1;
 ptr^.sNaam := 'IkPersoonlijk';
 lstGegevens.Add ( ptr );
end;


De code om gegevens uit deze lijst op te halen kan er zo uitzien;

procedure ToonInfo;
var i: integer;
begin
 for i:=0 to lstGegevens.Count - 1 do
  ShowMessage( Format( 'Nummer %d heet %s.',
   [ lstGegevens[i]^.iNummer, lstGegevens[i]^.sNaam ] ));
end;




Veiligere StrToFloat

Ook altijd zo'n ruzie met StrToFloat en punten of komma's als decimaalscheiding? Ik wel en ik heb daarvoor twee routines geschreven die de functie iets geschikter maakt als je niet weet of je nu een punt of een komma als decimaalscheider kunt verwachten. De functie heet 'MyStrToFloat' en maakt gebruik van een functie 'ConvertToLocalDecimalSeparator'. Deze functies zorgen er in ieder geval voor dat getallen als 12.34 of 12,34 hetzelfde geinterpreteerd worden. Let wel, de functie biedt geen oplossing voor getallen als 1.000,23 of 1,000.23.. maar dat soort getallen stop je maar in een try except blok ;-)

function ConvertToLocalDecimalSeparator( const sIn: string ): string;
var
 cDC: char;
begin
 Result := sIn;
 if DecimalSeparator = '.' then
  cDC := ','
 else
  cDC := '.';

 if Pos( cDC , sIn ) <> 0 then
  Result[ Pos( cDC, sIn ) ] := DecimalSeparator;
end;

function MyStrToFloat( sIn: string ): real;
begin
 Result := StrToFloat( ConvertToLocalDecimalSeparator( sIn ));
end;




ALLE messages naar je applicatie opvangen



Databasegegevens overpompen

Om gegevens van de ene naar de andere database over te pompen kun je gebruik maken van de handige TBatchMove component (standaard te vinden op het DataAccess tabblad).

TBatchMove

Dit component heeft een 'source' en een 'destination' property die verwijzen naar TTable componenten. Een andere belangrijke eigenschap is de 'Mode' welke aangeeft wat er met de data en tabellen moet gebeuren ( bv. kopiëren of toevoegen ). Door vervolgens de Execute procedure aan te roepen wordt de opdracht uitgevoerd.

De volgende code kopieert de gehele inhoud van een database naar een andere database.

Wat je nodig hebt:

  • De volgende units in het 'uses' gedeelte;

    Db, DBTables, BDE

  • Een procedure om alle tabelnamen uit een database te extraheren:

    procedure GetAllTables( lstTables: TStrings );
    var
     dbMain: TDatabase;
     hCursor: hDBICur;
     ListDesc: TBLBaseDesc;
    begin
     lstTables.Clear;
     db1 := nil;
     try
      dbMain := TDatabase.Create(nil);
      // Database eigenschappen
      dbMain.DatabaseName := 'De naam van de source database';
      dbMain.LoginPrompt := False;
      dbMain.Connected := True;
      Check(DbiOpenTableList(dbMain.Handle, False, False, '*', hCursor));
      while (DbiGetNextRecord(hCursor, dbiNOLOCK, @ListDesc, nil)
       = dbiErr_None) do
       if not( ListDesc.bView )then // queries overslaan
        lstTables.Add( ListDesc.szName );
      dbiCloseCursor(hCursor);
      db1.Connected := False;
     except
      dbMain.Free;
      raise;
     end;
     dbMain.Free;
    end;

    De code van GetAllTables is grotendeels gepikt van: www.latiumsoftware.com
  • Uiteraard twee databases (ik heb ze dbSource en dbDest genoemd), twee TTable componenten (tblSource en tblDest) waarbij deze uiteraard naar de gerelateerde database wijzen en een TBatchMove (bmMain) waarbij de source 'tblSource', de destination 'tblDest' en de Mode 'batUpdate' is.
De volgende procedure leest alle tabelnamen uit dbSource en kopieert alle data via de bmMain in dbDest.

procedure CopyDatabases;
var
 lstTables: TStringList;
 i: integer
begin
 lstTables := TStringList.Create;
//even er van uit gaan dat in GetAllTables de gegevens van dbSource staan
 GetAllTables( lstTables );
 for i:=0 to lstTables.Count - 1 do
 begin
  try
   tblSource.TableName := lstTables[i];
   tblDest.TableName := lstTables[i];
   bmMain.Execute; //de batchmove
  except
  on E:Exception do
  begin
   lstTables.Free;
   Raise;
  end;
 end;
 lstTables.Free;
end;


opmerking: Normaliter moet het kopiëren van de tabellen in de volgorde gebeuren als de het sql script waarmee de database aangemaakt is. Dit is van belang om de koppelingen in de database op orde te houden. In de praktijk betekent dit dat je een TStringList zult maken waaraan je de tabelnamen handmatig toevoegd in de volgorde als in het sql script van de database te vinden is.