Thanks
Trevor
I have no way to know what the objects are, exactly, but it does occur
to me that perhaps you could write the name of the object to a stream
and then (maybe!) call the "SaveToStream" method for that object ... and
thereby somehow come fairly close to getting where you want to go. Your
corresponding restore logic would have to be able to read the name from
the stream, create the object, have it read-from-stream ... umm, as I
write this, I must admit it's sounding hairier and hairier, trickier and
trickier, all the time.
/mr/
------------------------------------------------------------------
Sundial Services :: Scottsdale, AZ (USA) :: (480) 946-8259
mailto:in...@sundialservices.com (PGP public key available.)
> Fast(!), automatic table-repair with two clicks of the mouse!
> ChimneySweep(R): "Click click, it's fixed!" {tm}
> http://www.sundialservices.com/products/chimneysweep
>Does anyone know how to save StringGrid text and objects in cells to and
>from a file. I have to save the grid on every change to a cell and
>restore it in the event of a crash or power failure. I have got the text
>part working (sort of) but not the objects.
>I'm using Delphi 4.
>
Depends on what your objects are. If they are complex then it may be difficult,
as every property of the object must be saved. Give us a bit more detail on
this aspect.
Alan Lloyd
alang...@aol.com
Trevor Southam <dev...@starcomms.co.uk> wrote in message
news:3978AC3F...@starcomms.co.uk...
> Hi
> Does anyone know how to save StringGrid text and objects in cells to and
> from a file. I have to save the grid on every change to a cell and
> restore it in the event of a crash or power failure. I have got the text
> part working (sort of) but not the objects.
> I'm using Delphi 4.
>
> Thanks
>
> Trevor
>
>
Trevor
AlanGLLoyd wrote:
> In article <3978AC3F...@starcomms.co.uk>, Trevor Southam
> <dev...@starcomms.co.uk> writes:
>
> >Does anyone know how to save StringGrid text and objects in cells to and
> >from a file. I have to save the grid on every change to a cell and
> >restore it in the event of a crash or power failure. I have got the text
> >part working (sort of) but not the objects.
> >I'm using Delphi 4.
> >
>
end;
method similar for other file types
sg.objects[0,0] := pointer(ord(clRed));
g := integer(sg.objects[0,0]);
Trevor Southam <dev...@starcomms.co.uk> wrote in message
news:3979681E...@starcomms.co.uk...
Trevor
Thanks for all the input guys.
>What I want to save is the colour of the cell along with the text in it. The
>object
>is a pointer to say clBlue or clRed for example. I expect this makes the
>problem
>worse.
>
Trevor
That's fairly easy, provided you've stored the color in the Objects as
TObject(clBlue) etc. Just remember that TStringGrid.Rows (and TStringGrid.Cols)
are a TStrings. You have to store the number of strings/objects before storing
the strings, and also the number of TStringGrid.Rows.
The following should do it - except for the Read/WriteStreamInt() &
Read/WriteStreamString(), I have only tested it for compile, but most of it is
straight-forward. It's split into a number of procedures for clarity and ease
of my writing, you would most likely put them all together as methods anyway.
function ReadStreamInt(Stream : TStream) : integer;
{returns an integer from stream}
begin
if Stream.Read(Result, SizeOf(Integer)) < SizeOf(Integer) then
Result := -1;
end;
function ReadStreamStr(Stream : TStream) : string;
{returns a string from the stream}
var
StrLen : integer;
begin
{get length of string}
StrLen := ReadStreamInt(Stream);
if StrLen > -1 then begin
{set string to get memory}
SetLength(Result, StrLen);
{read characters}
Stream.Read(Result[1], StrLen);
end
else
Result := '';
{end; if StrLen > -1 else}
end;
procedure WriteStreamInt(Stream : TStream; Num : integer);
{writes an integer to the stream}
begin
Stream.Write(Num, SizeOf(Integer));
end;
procedure WriteStreamStr(Stream : TStream; Str : string);
{writes a string to the stream}
var
StrLen : integer;
begin
{get length of string}
StrLen := Length(Str);
{write length of string}
WriteStreamInt(Stream, StrLen);
{write characters}
Stream.Write(Str[1], StrLen);
end;
procedure SaveStringsColorToStream(SL : TStrings; Stream : TStream);
{saves strings and color objects from TStringList to stream
stream must be positioned ready to receive the data,
and is left positioned after writing the data}
var
StrCount, i : integer;
CellColor : TColor;
begin
StrCount := SL.Count;
WriteStreamInt(Stream, StrCount);
for i := 0 to SL.Count - 1 do begin
WriteStreamStr(Stream, SL.Strings[i]);
CellColor := TColor(SL.Objects[i]);
WriteStreamInt(Stream, CellColor);
end;
end;
procedure LoadStringsColorFromStream(SL : TStrings; Stream : TStream);
{loads strings and color objects to a TStringList from a stream
stream must be positioned at start of appropriate data,
and is left positioned after the data}
var
StrCount, i : integer;
Str : string;
CellColor : TColor;
begin
StrCount := ReadStreamInt(Stream);
for i := 0 to StrCount - 1 do begin
Str := ReadStreamStr(Stream);
CellColor := TColor(ReadStreamInt(Stream));
SL.AddObject(Str, TObject(CellColor));
end;
end;
procedure SaveStringGridToStream(SG : TStringGrid; Stream : TStream);
{saves string grid cell contents and TColor objects to a stream}
var
RowCount, i : integer;
begin
Stream.Seek(0, soFromBeginning);
RowCount := SG.RowCount;
WriteStreamInt(Stream, RowCount);
for i := 0 to SG.RowCount - 1 do
SaveStringsColorToStream(SG.Rows[i], Stream);
end;
procedure LoadStringGridFromStream(SG : TStringGrid; Stream : TStream);
{loads stringgrid from a stream conttaining strings and Tcolor objects}
var
RowCount, i : integer;
SL : TStringList;
begin
Stream.Seek(0, soFromBeginning);
RowCount := ReadStreamInt(Stream);
SL := TStringList.Create;
if SG.RowCount <> RowCount then
SG.RowCount := RowCount;
for i := 0 to RowCount - 1 do begin
SL.Clear;
LoadStringsColorFromStream(SL, Stream);
SG.Rows[i].Assign(SL);
end;
SL.Free;
end;
procedure SaveStringGridToFile(SG : TStringGrid; FileName : string);
var
FS : TFileStream;
begin
FS := TFileStream.Create(FileName, fmCreate or fmShareExclusive);
SaveStringGridToStream(SG, FS);
FS.Free;
end;
procedure LoadStringGridFromFile(SG : TStringGrid; FileName : string);
var
FS : TFileStream;
begin
FS := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
LoadStringGridFromStream(SG, FS);
FS.Free;
end;
Alan Lloyd
alang...@aol.com
>The following should do it -
Trevor
Whooopppsss - bit of brain GPF there.
The fact that you use the object value as a TColor has nothing to do with
saving and loading it to a stringgrid. And I did not restore the column count
of the stringgrid. Here is one I did later - and has been tested <gg>.
//= Save & Load StringGrid Strings and Object Integers ====
procedure SaveStringsObjectsToStream(SL : TStrings; Stream : TStream);
{saves strings and objects from TStringList to stream
stream must be positioned ready to receive the data,
and is left positioned after writing the data}
{Note :- Only the object value is saved, this is only meaningful if it has
been used as an integer value, as an object pointer it is meaningless}
var
StrCount, i, Obj : integer;
begin
StrCount := SL.Count;
WriteStreamInt(Stream, StrCount);
for i := 0 to SL.Count - 1 do begin
WriteStreamStr(Stream, SL.Strings[i]);
Obj := integer(SL.Objects[i]);
WriteStreamInt(Stream, Obj);
end;
end;
procedure LoadStringsObjectsFromStream(SL : TStrings; Stream : TStream);
{loads strings and objects to a TStringList from a stream
stream must be positioned at start of appropriate data,
and is left positioned after the data}
{Note :- Only the object value is loaded, this is only meaningful if it is
used as an integer value, as an object pointer it is meaningless}
var
StrCount, i, Obj : integer;
Str : string;
begin
StrCount := ReadStreamInt(Stream);
for i := 0 to StrCount - 1 do begin
Str := ReadStreamStr(Stream);
Obj := ReadStreamInt(Stream);
SL.AddObject(Str, TObject(Obj));
end;
end;
procedure SaveStringGridToStream(SG : TStringGrid; Stream : TStream);
{saves string grid cell contents and TColor objects to a stream}
var
ColCount, RowCount, i : integer;
begin
Stream.Seek(0, soFromBeginning);
ColCount := SG.ColCount;
WriteStreamInt(Stream, ColCount);
RowCount := SG.RowCount;
WriteStreamInt(Stream, RowCount);
for i := 0 to SG.RowCount - 1 do
SaveStringsObjectsToStream(SG.Rows[i], Stream);
end;
procedure LoadStringGridFromStream(SG : TStringGrid; Stream : TStream);
{loads stringgrid from a stream containing strings and Tcolor objects}
var
ColCount, RowCount, i : integer;
SL : TStringList;
begin
Stream.Seek(0, soFromBeginning);
ColCount := ReadStreamInt(Stream);
SG.ColCount := ColCount;
RowCount := ReadStreamInt(Stream);
SG.RowCount := RowCount;
SL := TStringList.Create;
for i := 0 to RowCount - 1 do begin
SL.Clear;
LoadStringsObjectsFromStream(SL, Stream);
SG.Rows[i].Assign(SL);
end;
SL.Free;
end;
procedure SaveStringGridToFile(SG : TStringGrid; FileName : string);
var
FS : TFileStream;
begin
FS := TFileStream.Create(FileName, fmCreate or fmShareExclusive);
SaveStringGridToStream(SG, FS);
FS.Free;
end;
procedure LoadStringGridFromFile(SG : TStringGrid; FileName : string);
var
FS : TFileStream;
begin
FS := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
LoadStringGridFromStream(SG, FS);
FS.Free;
end;
//=========================================================
Alan Lloyd
alang...@aol.com
var f : textfile;
begin
assignfile(f,MyFile);
rewrite(f);
for nn := 0 to sg.cols.count -1 do begin
for nCount := 0 to sg.rows.count -1 do begin
writeln(f, sg.cells[nn,ncount];
writeln(f, integer(sg.objects[nN,nCount]));
end;
end;
closefile(f);
end;
This was close to what I was attempting to do and it works.
The question is ... which would be more robust yours or the above ?
You code looks more universal as to the types of objects being streamed (I think).
I'm no expert on this as the only streaming I've done has been text based.
I shall certainly go away and play with this to learn more about how it works.
Thanks again Alan
Trevor
>I'm no expert on this as the only streaming I've done has been text based.
Regardless of the storage format, you can wind up the problem from the opposite
side: how to restore an object from a stream? Then you'll find statements like:
//for each cell...
grid.Strings[x,y] := ReadTheString(stream);
ObjType := ReadTheObjType(stream);
if ObjType <> nil then begin
grid.Objects[x,y] := ObjType.Create;
grid.Objects[x,y].LoadFromStream(stream);
end;
//loop...
Saving the objects then will store the object type (class) and data in the same
way, as it's read back by the loading code later.
When all objects are of the same class, you can omit the step to store and read
the class type, and create objects of the predefined class. Likewise the
information for every object can be read by the appropriate statements, as the
information was stored in the stream before.
For more flexibility, create a base class for all intended objects, which
includes at least the SaveToStream and LoadFromStream methods. The creation of
the objects can be encapsulated in a class method CreateFromStream, which reads
the object type, creates the according object, calls it's LoadFromStream
method, and returns the new object.
In your case, the resulting base class could look like:
TStreamableObject = class
public
class function CreateFromStream(stream: TStream): TObject;
procedure LoadFromStream(stream: TStream); virtual; abstract;
procedure SaveToStream(stream: TStream); virtual; abstract;
...
end;
class function TStreamableObject.CreateFromStream;
var c: char;
begin
stream.Read(c, 1); //means: ReadTheObjectType
case c of
'c': //meaning a "color" object
stream.Read(Result, sizeof(TColor)); //very dirty!
'C': //requires a Class TColorObject(TStreamableObject)...
begin
Result := TColorObject.Create;
Result.LoadFromStream(stream);
//which does: stream.Read(self.Color, sizeof(self.Color));
end;
//more cases as required...
else
Result := nil;
end;
end;
TColorObject = class(TStreamableObject)
public
FColor: TColor;
procedure LoadFromStream(stream: TStream); override;
procedure SaveToStream(stream: TStream); override;
...
end;
procedure TColorObject.LoadFromStream;
begin
stream.Read(FColor, sizeof(FColor);
end;
procedure TColorObject.SaveToStream;
begin
//optionally: write a class type indicator
stream.Write('C', 1); //as appropriate
//mandatory: the data
stream.Write(FColor, sizeof(FColor));
//write more as appropriate for other objects...
end;
The write loop should write a predefined character to the stream for every Nil
object, so that the CreateFromStream can read a character (or integer or
whatsoever...) even for nonexisting objects. You also can substitute TStream by
a Text file, and read/write the information in the appropriate format, as you
used to do before.
It's up to you to keep in sync the writing and reading of any information in
the stream.
DoDi
Regards
Trevor
VBDis wrote:
> Im Artikel <397ACA44...@starcomms.co.uk>, Trevor Southam
> <dev...@starcomms.co.uk> schreibt:
>
> >I'm no expert on this as the only streaming I've done has been text based.
>