Best regards
Magnus
> I have a combobox filled with stringvalues. Each stringitem has a small
> object associated. I want to save the combobox Items(TStrings) with
> the object to the disk and of course also be able read them back from
> disk.
You have to manually loop through the Items reading/writing the file
yourself, since only you know what kind of objects are stored in the Items
to begin with.
Gambit
TStringList.Objects is just a list of pointers to memory locations --
Since they can point to anything (I even use them to just store
integers, which makes them invalid as TObjects) you have to write your
own code to handle streaming them.
--
Brandon Staggs
http://www.swordsearcher.com
http://www.studylamp.com
http://www.brandonstaggs.com
"Remy Lebeau (TeamB)" <no....@no.spam.com> skrev i en meddelelse
news:481fff52$1...@newsgroups.borland.com...
> I was aware of that i had to write code on my own to stream the objects, but i was looking for examples to do so as i am not so
> aquainted with using TStreamList, TReader, TWriter etc.
Why not use xml rather than a binary stream?
Binary streams are har to maintain over time: you'll at least
need some kind of versioning to be able to deal with future changes
TReader and TWriter are only intended for streaming the dfm's.
I would not suggest using those.
I you go for binary, check for example
procedures TCustomTreeView.CreateWnd (reading) and
procedures TCustomTreeView.DestroyWnd (writing)
both in unit ComCtrls.pas
This shows how to create a (memory) stream and store / read some data on it.
Instead of a memorystream you could use a filestream, or even better:
store the memorystream to a filestream to get some kind of buffering.
But as said: I recommend plain text such as xml.
Gerrit Beuze
ModelMaker Tools
Boost your productivity in the Delphi IDE with ModelMaker Code Explorer:
http://www.modelmakertools.com/code-explorer/index.html
> I was aware of that i had to write code on my own to stream the objects,
> but i was looking for examples to do so
See further below.
> as i am not so aquainted with using TStreamList, TReader, TWriter etc.
Although you could use TReader and TWriter, I would suggest using TStream
directly instead. TReader and TWriter are primarily meant for DFM
streaming, which you are not using.
> So i was looking for an example to guide me in my struggling.
What exactly are you struggling with, though? To save the list, simply
write out the number of items in the list, then loop through the list
calling TStoneObj.Save() on each object. To load the list, simply read the
number of item, then loop that many times creating TStoneObj instances,
calling TStoneObj.Load() and AddObject() for each one. For example:
procedure WriteStringToStream(const S: String; Stream: TStream);
var
Len: Integer;
LS: UTF8String;
begin
// D2008 will have native UTF-8 support (finally)...
LS := {$IFDEF VER200}S{$ELSE}UTF8Encode(S){$ENDIF};
Len := Length(LS);
Stream.WriteBuffer(@Len, SizeOf(Len));
Stream.WriteBuffer(Pointer(LS)^, Len * SizeOf(AnsiChar));
end;
procedure SaveItemsToStream(Items: TStrings; Stream: TStream);
var
Count, I: Integer;
begin
Count := Items.Count;
Stream.WriteBuffer(@Count, SizeOf(Count));
for I := 0 to Count-1 do
begin
WriteStringToStream(Items.Strings[I], Stream);
TStoneObj(Items.Objects[I]).Save(Stream);
end;
end;
procedure SaveItemsToFile(Items: TStrings; const FileName: String);
var
Strm: TFileStream;
begin
Strm := TFileStream.Create(FileName, fmCreate);
try
try
SaveItemsToStream(Items, Strm);
finally
Strm.Free;
end;
except
DeleteFile(FileName);
raise;
end;
end;
begin
SaveItemsToFile(ComboBox1.Items, 'myfile.dat');
end;
Then to load the Items back:
function ReadStringFromStream(Stream: TStream): String;
var
Len: Integer;
LS: UTF8String;
begin
Stream.ReadBuffer(@Len, SizeOf(Len));
SetLength(LS, Len);
if Len > 0 then
begin
Stream.ReadBuffer(Pointer(LS)^, Len * SizeOf(AnsiChar));
// D2008 will have native UTF-8 support (finally)...
Result := {$IFDEF VER200}LS{$ELSE}UTF8Decode(LS){$ENDIF};
end else
Result := '';
end;
procedure ReadItemsFromStream(Items: TStrings; Stream: TStream);
var
Count, I: Integer;
S: String;
Obj: TStoneObj;
begin
Items.BeginUpdate;
try
Count := Items.Count;
for I := 0 to Count-1 do TStoneObj(Items.Objects[I]).Free;
Items.Clear;
Stream.ReadBuffer(@Count, SizeOf(Count));
for I := 0 to Count-1 do
begin
S := ReadStringFromStream(Stream);
Obj := TStoneObj.Create;
try
Obj.Load(Stream);
Items.AddObject(S, Obj);
except
Obj.Free;
raise;
end;
end;
finally
Items.EndUpdate;
end;
end;
procedure ReadItemsFromFile(Items: TStrings; const FileName: String);
var
Strm: TFileStream;
begin
Strm := TFileStream.Create(FileName, fmOpenRead or mShareDenyWrite);
try
ReadItemsFromStream(Items, Strm);
finally
Strm.Free;
end;
end;
begin
ReadItemsFromFile(ComboBox1.Items, 'myfile.dat');
end;
> I suppose i must loop through the StringList with a for loop, and retreive
> each associated object and write to the stream.
Yes.
Gambit
"Gerrit Beuze" <gerrit[at][-nospam]modelmakertools[dot]com> skrev i en
meddelelse news:4822da04$1...@newsgroups.borland.com...
Best regards
Magnus S Petersen
"Remy Lebeau (TeamB)" <no....@no.spam.com> skrev i en meddelelse
news:48235f34$1...@newsgroups.borland.com...
TStoneListObj = class
private
FStoneList:TStringList;
FStoneObj:TStoneObj;
destructor destroy;
published
constructor create;
procedure AddNewStone;
procedure ChangeStone;
procedure DeleteStone;
procedure SaveStoneListToStream;
procedure LoadStoneListFromStream;
property StoneList:TStringList read FStoneList;
end;
var
StoneObj:TStoneObj;StoneListObj:TStoneListObj;
{------------------------------------------------------------------}
{ Implementing the TStoneObject used in program }
{------------------------------------------------------------------}
procedure TStoneObj.Load;
begin
// ?????
end;
procedure TStoneObj.Save;
begin
// ?????
end;
{------------------------------------------------------------------}
{ Implementing the StoneList object used in program }
{------------------------------------------------------------------}
constructor TStoneListObj.create;
begin
inherited create;
FStoneList:=TstringList.Create;
FStoneObj:=TStoneObj.Create;
end;
destructor TStoneListObj.destroy;
begin
FStoneList.free;
FStoneObj.free;
end;
procedure TStoneListObj.AddNewStone;
var
aStoneObj:TStoneObj;
begin
{First we create the stone object}
aStoneObj:=TStoneObj.Create;
with aStoneObj do
begin
Name :=MainForm.cbxStoneName.Text;
Pseudonym :=MainForm.memPseudonyms.Lines.CommaText;
PictureName :=MainForm.cbxPictureName.Text;
if MainForm.rgStoneType.ItemIndex<>-1 then
StoneType
:=MainForm.rgStoneType.Items[MainForm.rgStoneType.ItemIndex];
Country :=MainForm.cbxCountry.Text;
if MainForm.rgColor.ItemIndex<>-1 then
Color :=MainForm.rgColor.Items[MainForm.rgColor.ItemIndex];
//Picture :=
OnStock :=IntToStr(MainForm.rgOnStock.ItemIndex);
Price :=MainForm.edPrice.Text;
end;
{Second we add the stone object to the list}
FStoneList.AddObject(aStoneObj.Name,aStoneObj);
end;//Works fine.
procedure TStoneListObj.ChangeStone;
begin
//implement later
end;
procedure TStoneListObj.DeleteStone;
begin
//implement later
end;
procedure TStoneListObj.LoadStoneListFromStream;
begin
//
end;
procedure TStoneListObj.SaveStoneListToStream;
begin
//
end;
Thank you very, very much in advance.
Best regards
Magnus
"Remy Lebeau (TeamB)" <no....@no.spam.com> skrev i en meddelelse
news:48235f34$1...@newsgroups.borland.com...
"Remy Lebeau (TeamB)" <no....@no.spam.com> skrev i en meddelelse
news:48235f34$1...@newsgroups.borland.com...
> I still have difficulties in this streaming business.
Again, what EXACTLY are you having problems with? You never answered that
question when I asked you earlier.
> Please find below my to objects. Can you have a look
> and finish the streaming process.
Asside from the syntax errors in your code, you are not even close to
implementing the streaming properly. Did you even try the code I gave you?
For example:
type
TStoneObj = class
public
Name,
Pseudonym,
PictureName,
StoneType,
Country,
Color,
Picture,
OnStock,
Price: String;
procedure LoadFromStream(AStream: TStream);
procedure SaveToStream(AStream: TStream);
end;
TStoneListObj = class
private
FStoneList: TStringList;
...
public
constructor Create;
destructor Destroy; override;
...
procedure SaveToStream(AStream: TStream);
procedure LoadFromStream(AStream: TStream);
...
end;
function ReadStringFromStream(Stream: TStream): String;
var
Len: Integer;
LS: UTF8String;
begin
Stream.ReadBuffer(@Len, SizeOf(Len));
SetLength(LS, Len);
if Len > 0 then
begin
Stream.ReadBuffer(Pointer(LS)^, Len * SizeOf(AnsiChar));
// D2008 will have native UTF-8 support (finally)...
Result := {$IFDEF VER200}LS{$ELSE}UTF8Decode(LS){$ENDIF};
end else
Result := '';
end;
procedure WriteStringToStream(const S: String; Stream: TStream);
var
Len: Integer;
LS: UTF8String;
begin
// D2008 will have native UTF-8 support (finally)...
LS := {$IFDEF VER200}S{$ELSE}UTF8Encode(S){$ENDIF};
Len := Length(LS);
Stream.WriteBuffer(@Len, SizeOf(Len));
Stream.WriteBuffer(Pointer(LS)^, Len * SizeOf(AnsiChar));
end;
procedure TStoneObj.LoadFromStream(AStream: TStream);
begin
Name := ReadStringFromStream(AStream);
Pseudonym := ReadStringFromStream(AStream);
PictureName := ReadStringFromStream(AStream);
StoneType := ReadStringFromStream(AStream);
Country := ReadStringFromStream(AStream);
Color := ReadStringFromStream(AStream);
Picture := ReadStringFromStream(AStream);
OnStock := ReadStringFromStream(AStream);
Price := ReadStringFromStream(AStream);
end;
procedure TStoneObj.SaveToStream(AStream: TStream);
begin
WriteStringToStream(Name, AStream);
WriteStringToStream(Pseudonym, AStream);
WriteStringToStream(PictureName, AStream);
WriteStringToStream(StoneType, AStream);
WriteStringToStream(Country, AStream);
WriteStringToStream(Color, AStream);
WriteStringToStream(Picture, AStream);
WriteStringToStream(OnStock, AStream);
WriteStringToStream(Price, AStream);
end;
constructor TStoneListObj.Create;
begin
inherited Create;
FStoneList := TStringList.Create;
...
end;
destructor TStoneListObj.Destroy;
begin
...
FStoneList.Free;
inherited Destroy;
end;
procedure TStoneListObj.LoadFromStream(AStream: TStream);
var
Count, I: Integer;
aStoneObj: TStoneObj;
begin
FStoneList.BeginUpdate;
try
Count := FStoneList.Count;
for I := 0 to Count-1 do TStoneObj(FStoneList.Objects[I]).Free;
FStoneList.Clear;
AStream.ReadBuffer(@Count, SizeOf(Count));
for I := 0 to Count-1 do
begin
aStoneObj := TStoneObj.Create;
try
aStoneObj.LoadFromStream(AStream);
FStoneList.AddObject(aStoneObj.Name, aStoneObj);
except
aStoneObj.Free;
raise;
end;
end;
finally
FStoneList.EndUpdate;
end;
end;
procedure TStoneListObj.SaveToStream(AStream: TStream);
var
Count, I: Integer;
begin
Count := FStoneList.Count;
AStream.WriteBuffer(@Count, SizeOf(Count));
for I := 0 to Count-1 do
TStoneObj(FStoneList.Objects[I]).SaveToStream(Stream);
end;
Gambit
[DCC Error] ObjectUnit.pas(94): E2197 Constant object cannot be passed as
var parameter
Is there a compiler directive i have missed or what do you think is the
reason for not accepting the pointer reference
Best regards
Magnus
"Remy Lebeau (TeamB)" <no....@no.spam.com> skrev i en meddelelse
news:48288985$1...@newsgroups.borland.com...
> When i compile the code now, with the
> implementation of the streaming process, i get an error on the @
> operator in the function
>
> Stream.ReadBuffer(@Len, SizeOf(Len));
>
> [DCC Error] ObjectUnit.pas(94): E2197 Constant object cannot be
> passed as var parameter
>
> Is there a compiler directive i have missed or what do you think is
> the reason for not accepting the pointer reference
Remove the "@" and it will compile.
The same applies to the ones in WriteStringToStream, LoadFromStream and
SaveToStream.
--
Pieter
"Pieter Zijlstra" <p.zylstr...@hccnet.nl> skrev i en meddelelse
news:xn0fq3wqe...@newsgroups.codegear.com...