I am using Delphi 7 and playing with a StringList, with TStream as object.
My test project has a ListBox, a Memo and 2 buttons (Add and Remove).
Here is what I got so far:
var
List: TStringList;
procedure TForm1.FormCreate(Sender: TObject);
begin
List := TStringList.Create;
end;
procedure TForm1.FormDestroy(Sender: TObject);
var
I: Integer;
begin
if (List.Count > 0) then
for I := 0 to Pred(List.Count) do
begin
List.Objects[I].Free;
List.Objects[I] := nil;
end;
FreeAndNil(List);
end;
procedure TForm1.btnAddClick(Sender: TObject);
var
Strm: TStream;
begin
Strm := TMemoryStream.Create;
try
Memo.Lines.SaveToStream(Strm);
List.AddObject(IntToStr(List.Count), TObject(Strm));
Memo.Clear;
ListBox.Items.Assign(List);
finally
// Strm.Free; (line removed)
end;
end;
procedure TForm1.btnDelFirstClick(Sender: TObject);
begin
if (List.Count > 0) then
begin
List.Objects[0].Free;
List.Objects[0] := nil;
List.Delete(0);
ListBox.Items.Assign(List);
end;
end;
When I double-click the ListBox I would like to load the selected item Stream object to Memo. Here is what I tried to do:
procedure TForm1.ListBoxDblClick(Sender: TObject);
var
Idx: Integer;
begin
Memo.Clear;
Idx := ListBox.ItemIndex;
if (Idx >= 0) and (TStream(List.Objects[Idx]).Size > 0) then
Memo.Lines.LoadFromStream(TStream(List.Objects[Idx]));
end;
My questions are:
Is correct the way I am adding and removing (freeing) the TStream object inside the StringList? Maybe I need to first free the Stream and then the Object??
Is correct the way I am freeing all objects on FormDestroy event?
When I try to load the stream back to Memo (Memo.Lines.LoadFromStream(TStream(List.Objects[Idx]))), it doesn't load, despite Stream.Size is higher than zero. What I am doing wrong?
Yes, because the
TStrings.Objects[]property returns aTObjectpointer andTStreamderives fromTObject, so you can callFree()on the object pointers.You need to free the
TStreamobjects before freeing theTStringListobject. Just as you are already doing.Yes. Though technically, you do not need to check the
TStringList.Countproperty for> 0before entering the loop, as the loop will handle that condition for you. And you do not need tonilthe pointers before freeing theTStringList:One thing you are doing that is overkill, though, is
Assign()ing the entireTStringListto theTListBoxwhenever you add/delete a single item from theTStringList. You should instead simply add/delete the associated item from the ListBox and preserve the remaining items as-is.And add some extra error checking to
btnAddClick()as well to avoid any memory leaks if something goes wrong.Try this:
You are not seeking the stream back to
Position0 before loading it into the Memo.SaveToStream()always leaves the stream positioned at the end of the stream, andLoadFromStream()leave the stream positioned wherever the load stopped reading from (if not at the end, in case of failure).Now, with all of this said, I personally would not use
TListBoxin this manner. I would instead set itsStyleproperty tolbVirtualand then use itsOnDataevent to display the strings from theTStringList. No need to copy them into theTListBoxdirectly, or try to keep the two lists in sync at all times. It would be safer, and use less memory, to let theTListBoxask you for what it needs, and then you can provide it from theTStringList(which I would then change to aTListsince you are not really storing meaningful names that can't be produced dynamically in theOnDataevent handler):