Martin: Du har ret, IndexOf kigger på strengen. Pos og PosEx (StrUtils) er bedre. Endnu bedre var det at smide data som et objekt pi en TObjectList (ContNrs) (jeg gentager mig selv, men her er det helt oplagt)
Derfor:
Jeg plejer at vakle mellem to løsninger:
- At opbevare objekterne parallelt i en TObjectList. Fordelen er, at når denne frigives, så ryger alle dataobjekterne også. En anden er at det er let at vedligeholde. Ulempen er at koden fylder en smule mere.
- Kun have objekterne i listen. Så må disse oprettes i formens constructor (OnCreate) og frigives i destructoren (OnDestroy)
Kan bedst lide den første da indkapslingen er bedst og man kan indbygge ind- og udlæsning her. Her kommer denne løsning:
type
TNameData = class
private
public
constructor Create(aReader: TReader); overload; // Indl. fra fil
constructor Create(const aInit, aCountry, aTown: string); overload; // Ny
property Init: string read fInit;
property Country: string read fCountry;
property Town: string read fTown;
procedure WriteData(aWriter: TWriter);
end;
TNameList = class(TObjectList)
private
function GetItems(const aIndex: integer): TNameData;
public
property Items[const aIndex: integer]: TNameData read GetItems; default;
procedure LoadData;
procedure SaveData;
IndexOfInit(aInit: string): integer;
IndexOfCountry(aCountry: string): integer;
IndexOfTown(aTown: string): integer;
FillStrings(aStrings: TStrings);
end;
TForm1 = class(TForm)
private
fNameList: TNameList;
public
end;
..
constructor TForm1.OnCreate(Sender: TObject);
var
i: integer;
begin
fNameList := TNameList.Create;
fNameList.LoadData;
fNameList.FillStings(ComboBox.Items);
end;
destructor TForm1.Destroy;
begin
ComboBox.Items.Clear; // Events can be triggered, better clear before freeing
fNameList.Free;
end;
...
constructor TNameData.Create(const aInit, aCountry, aTown: string);
begin
inherited Create;
fInit := UpperCase(trim(aInit));
fCountry := UpperCase(trim(aCountry));
fTown := UpperCase(trim(aTown));
end;
constructor TNameData.Create(aReader: TReader);
begin
inherited Create;
fInit := aReader.ReadString;
fCountry := aReader.ReadString;
fTown := aReader.ReadString;
end;
procedure TNameData.WriteData(aWriter: TWriter);
begin
aWriter.WriteString(fInit);
aWriter.WriteString(fCountry);
aWriter.WriteString(fTown);
aWriter.FlushBuffer; // Sync stream.
end;
...
const
DataFilename = 'c:\test.dat';
procedure TNameList.LoadData;
var
Stream: TFileStream;
Reader: TReader;
begin
if not FileExists(DataFilename) then
exit;
Stream := TFileStream.Create(DataFilename,fmOpenRead);
Reader := TReader.Create(Stream,1024);
BeginUpdate;
try
Clear;
while Stream.Position < Stream.Size do
Add(TNameData.Create(Reader));
// - en lidt simplere måde at indlæse på
// Add(TNameData.Create('','',''));
// Add(TNameData.Create('','',''));
// Add(TNameData.Create('','',''));
finally
Reader.Free;
Stream.Free;
EndUpdate;
end;
end;
procedure TNameList.SaveData;
var
Stream: TFileStream;
Writer: TWriter;
begin
Stream := TFileStream.Create(DataFilename,fmCreate);
Writer := TWriter.Create(Stream,1024);
try
for i := 0 to Count - 1 do
Items[i].WriteData(Writer);
Writer.FlushBuffer; // Sync again (to be sure)
finally
Writer.Free;
Stream.Free;
end;
end;
function TNameList.GetItems(const aIndex: integer): TNameData;
begin
result := inherited Items[aIndex] as TNameData;
end;
procedure TNameList.FillStrings(aStrings: TStrings);
var
i: integer;
NameData: TNameData;
begin
aStrings.BeginUpdate;
try
aStrings.Clear;
for i := 0 to Count - 1 do
begin
NameData := Items[i];
aStrings.AddObject(NameData.ToString,NameData);
end;
finally
aStrings.EndUpdate;
end;
end;
function TNameList.IndexOfInit(aInit: string): integer;
var
Found: boolean;
begin
result := -1; // not found
while (result < Count - 1) and not Found do
begin
inc(result);
Found := SameText(Items[i].Init,aInit);
end;
if not Found then
result := -1;
end;
function TNameList.IndexOfCountry(aCountry: string): integer;
var
Found: boolean;
begin
result := -1; // not found
while (result < Count - 1) and not Found do
begin
inc(result);
Found := SameText(Items[i].Country,aCountry);
end;
if not Found then
result := -1;
end;
function TNameList.IndexOfTown(aTown: string): integer;
var
Found: boolean;
begin
result := -1; // not found
while (result < Count - 1) and not Found do
begin
inc(result);
Found := SameText(Items[i].Town,aTown);
end;
if not Found then
result := -1;
end;
Det kan godt være det fylder lidt, men det jeg har lavet her er 90% af det jeg mener er den rigtige løsning (nedarving fra TPersistent eller ej). De data du skitserer bør splittes op i en en klasse der indeholder hvert element. Viser også hvordan man laver overloadede metoder og hvordan man flytter funktionaliteten ned i de lag som ved hvordan de skal håndteres (WriteData(aWriter: TWriter), eksempelvis).
Det er skrevet i et stykke da det er noget jeg har brugt mange gange - slåfejl kan ikke udelukkes