My TPanel panel adds other panels in the designer and adds them to the internal list. But when deleting the internal panels I do not understand how to remove from the internal list. When adding, I use the editor

 procedure TShiftPanelEditor.ExecuteVerb(Index: Integer); var zPanel: TPanelEx; begin case Index of 0: begin zPanel := Designer.CreateComponent(TPanelEx, Component, -1, -1, -1, -1) as TPanelEx; zPanel.ShiftingPanel := (Component as TShiftingPanel); end; else inherited; end; end; 

According to the zPanel.ShiftingPanel, a setter is defined that inserts the panel into the internal list

 procedure TPanelEx.SetPanel(const Value: TShiftingPanel); begin Value.InsertPanel(Self); end; 

How to organize the correct removal?

  • Is the TShiftingPanel installed in TPanelEx as a private field? - kami

2 answers 2

There is a more general and (in my opinion) more elegant way of tracking the removal of nested controls. Everything else - it allows you to keep up to date the internal list not only when the object is released, but also when its parent changes (for example, if the nested panel moves to another TShiftingPanel ):

 type TShiftingPanel = class(TPanel) private FPanels: TList; procedure CMControlChange(var Message: TMessage); message CM_CONTROLCHANGE; end; procedure TShiftingPanel.CMControlChange(var Message: TMessage); begin // обрабатываем сообщение о том, что нам "внутрь" пытаются добавить // или удалить компонент // через установку свойства Parent. // это же сообщение отправляется при удалении вложенного компонента // т.к. при его уничтожении выставляется Parent = nil. if TObject(message.WParam) is TPanelEx then if Boolean(message.LParam) then begin // контрол добавляется FPanels.Add(Pointer(message.WParam)); end else begin // контрол удаляется FPanels.Remove(Pointer(message.LParam)); end; inherited; end; 

Thus, there is no need for external tracking of the relevance of the FPanels list - TShiftingPanel itself will monitor this. And the nested panels will only have to set the Parent property.


In addition, I propose to get rid of the property TPanelEx.ShiftingPanel . Rather, do not get rid of, and leave only getter. If I understand correctly, then ShiftingPanel should return the panel on which PanelEx lies. And this is already known from the Parent property:

 TPanelEx = class(TPanel) public property ShiftingPanel: TShiftingPanel read GetShiftingPanel; end; function TPanelEx.GetShiftingPanel: TShiftingPanel; begin if Parent is TShiftingPanel then Result:=TShiftingPanel(Parent) else Result:=nil; end; 
  • I TShiftPanelEditor.ExecuteVerb trying to remake for your version, but in the TShiftPanelEditor.ExecuteVerb function of the editor an error occurs that the entry point to the ShiftingPanel procedure here on this line zShiftinfPanel := TPanelEx(Component).ShiftingPanel . The error message crashes when I try to install the component by install . ShiftingPanel - property with getter from your version - gregor
  • As everywhere they write that ExecuteVerb is called by clicking on the menu of this component, then why does an error fall out there? if you comment out the body, the error does not occur during install - gregor
  • Theoretically, the message should not be so (give the full text) - because ShiftingPanel is not a procedure, but a property that has only a getter. By the way, did you remember to declare a getter as a private function of an object? I'm not sure that automation exists in D7, but if you put the cursor in the body of the GetShiftingPanel function and press Ctrl + Shift+C , the declaration should automatically migrate to the class declaration in a private section. Alternatively, place the cursor inside the TPanelEx ad and press the same key combination. - kami
  • In dogonka - with CM_CONTROLCHANGE it turned out? I had big doubts about the availability of such functionality in the D7, although it existed without changes in the D2010 - XE7 line. - kami
  • With CM_CONTROLCHANGE it turned out. Here is the error code. The GetShiftingPanel GetShiftingPanel is and it is in the private section. Can I give the full code somehow? - gregor

Solved the problem as follows

 procedure TShiftPanelEditor.ExecuteVerb(Index: Integer); var zPanel: TPanelEx; zShiftinfPanel: TShiftingPanel; begin if Component is TShiftingPanel then zShiftinfPanel := TShiftingPanel(Component) else if Component is TPanelEx then zShiftinfPanel := TPanelEx(Component).ShiftingPanel else raise Exception.Create('Component is not TPanelEx'); case Index of 0: begin zPanel := TPanelEx.Create((Designer).GetRoot); zPanel.Parent := zShiftinfPanel; zPanel.ShiftingPanel := zShiftinfPanel; Designer.SelectComponent(zPanel); Designer.Modified; end; 1: if Component is TPanelEx then TPanelEx(Component).Free; end; end; function TShiftPanelEditor.GetVerb(Index: Integer): string; begin case Index of 0: Result := 'New panel'; 1: Result := 'Delete panel'; end; end; 

in TPanelEx destructor

 destructor TPanelEx.Destroy; begin if FShiftingPanel <> nil then FShiftingPanel.RemovePanel(Self); inherited; end 

and already in FShiftingPanel.RemovePanel(Self);

 procedure TShiftingPanel.RemovePanel(aPanel: TPanelEx); begin aPanel.ShiftingPanel := nil; FPanels.Remove(aPanel); end;