The below errors happen only when testing under MacOS. Under Windows it seems to work. I'm trying to display a dynamic list of strings into a TPanel, each inside it's own TPanel. The list of strings is kept in a TStringList object and everytime a new one is added or deleted, I'm calling a method to update the display by clearing what exists and then re-build the visual components. The update panel display method looks like this:
procedure TTestsScreen.UpdatePanelDisplay3;
var
i : Integer;
myPanel : TPanel;
myLabel : TLabel;
myImage : TImage;
begin
//dispose of existig entities
for i := (Length(search_entities_labels) - 1) downto 0 do
begin
search_entities_labels[i].Free;
search_entities_labels[i]:= nil;
end;
for i := (Length(search_entities_images) - 1) downto 0 do
begin
search_entities_images[i].Free;
search_entities_images[i]:= nil;
end;
for i := (Length(search_entities_panels) - 1) downto 0 do
begin
search_entities_panels[i].Free;
search_entities_panels[i]:= nil;
end;
//adjust dynamic arrays
SetLength(search_entities_images,main_search_entities_string_list.Count);
SetLength(search_entities_labels,main_search_entities_string_list.Count);
SetLength(search_entities_panels,main_search_entities_string_list.Count);
//rebuild
for i := 0 to main_search_entities_string_list.Count-1 do
begin
myPanel:= TPanel.Create(Self);
myPanel.Parent := Panel1;
myPanel.Height:= 30;
myPanel.Width:= Panel1.Width;
myPanel.Position.X:= 0;
myPanel.Position.Y:= i*30;
search_entities_panels[i]:= myPanel;
//
myLabel:= TLabel.Create(Self);
myLabel.Parent:= myPanel;
myLabel.Text:= main_search_entities_string_list[i];
myLabel.Width:= (3*myPanel.Width) / 4;//display left
search_entities_labels[i]:= myLabel;
//
myImage:= TImage.Create(Self);
myImage.Parent:= myPanel;
myImage.Tag:= i;
myImage.Width:= 15;
myImage.Height:= 15;
myImage.Position.X:= myPanel.Width - myImage.Width;//display right
myImage.MultiResBitmap.Assign(AppResources.delete_white_image.MultiResBitmap);
myImage.OnClick:= DeleteSearchEntity;
search_entities_images[i]:= myImage;
end;
//
Panel1.Height:= 30 * main_search_entities_string_list.Count;
end;
I'm adding new elements into the panel like this:
procedure TTestsScreen.AddClick(Sender: TObject);
begin
main_search_entities_string_list.Add(DateTimeToStr(now));
UpdatePanelDisplay3;
end;
The adding process works fine, with no errors. The problem is that when I try to delete an element like this
procedure TTestsScreen.DeleteSearchEntity(Sender: TObject);
begin
main_search_entities_string_list.Delete((Sender as TImage).Tag);
UpdatePanelDisplay3;
end;
I'm randomly getting
Runtime error 231
or
Exception EAccessViolation in module TestApp at 00039F33.
Access violation at address 00E7BBBC, accessing address 00000158.
Runtime error 0 at 00039F33
Inside the FormCreate event I'm doing
procedure TTestsScreen.FormCreate(Sender: TObject);
begin
SetLength(search_entities_labels,0);
SetLength(search_entities_images,0);
SetLength(search_entities_panels,0);
main_search_entities_string_list:= TStringList.Create;
end;
The arrays are declared as
search_entities_labels : array of TLabel;
search_entities_images : array of TImage;
search_entities_panels : array of TPanel;
You should not be Freeing a component from within an event handler that is being called by the component itself. In this case you are Freeing a TImage from within its OnClick event handler.
You have to split this operation in two phases.
Phase 1
In the event handler you either enqueue a message for the form or you just flag the component set (Image, Label and Panel) as "To Be Deleted". You can do this many ways... by keeping a list of components to be deleted... or of indexes into the array or TObjectList that contains them.
Phase 2
In the form, when you have time, you finally Free the unneeded components. You can do this when you receive the enqueued message (if you chose that technique) or you can use Application.OnIdle (if you just flagged them for deletion).
A further notice... I spoke of enqueued messages, which is IMHO the best solution. Beware that standard FireMonkey messages are not enqueued in an asynchronous way, but are immediately dispatched in a synchronous way. So they cannot solve your problem... you have to use TThread.Queue to snap out of the empasse.