4
votes

There's a container control, a TScrollBox, that parents multiple item controls.

Every item control, being compound itself, contains (parents & owns) a delete button. Pressing the button initiates the deleting of the item control.

The deleting involves freeing the component and so the actual operation should be extrinsic with regard to the item. The question is, what would be the best way to do it?

I actually know of a couple of options:

  • a timer with a small interval (which is started with the button click);
  • a hidden outside button (to which the mouse down & up messages are posted);
  • the form's custom message handler.

While I could confidently implement any of those methods, as I flatter myself, I'm not sure which one would be best. Besides, the timer option seems childish, the hidden button one hackish, and the custom message one a bit overkill. In short, all three seem equally half-acceptable, more or less.

I may simply be prejudiced and don't mind being convinced to the contrary. Still most of all I would like to know what is a common method to use in such cases (maybe something I've been missing all the time).

2
I know I'm probably missing something here. If the item contains the controls, wouldn't its own destructor get rid of its child components? If this is true, why not just removing the item and freeing it?Leonardo Herrera
The same reason I deleted my answer: The delete button Andriy mentions is part of the control to be deleted and would implement its own handling of the click on the delete button. So you would be doing it from one of the control's own event handlers and normally control's really don't like having the rug (the world) pulled out from underneath themselves...Marjan Venema
@Marjan is there a problem with asking a control to free it's self ?Najem
@Najem: Not in and of itself. Instances can free themselves. But it should be the last thing they do and all references to them should have been cleared before they do. Most of the time an instance doesn't know who/what is holding references to it and that means it could be asked to delete itself twice or more. Using an asynchronous message like in David's answer means that destruction gets postponed (preventing AV's) and once the control is freed, any further messages (including "delete yourself") to that control will/should be ignored as the handle is gone. (That about it @David?)Marjan Venema
@Najem: Just spotted your question on this. Mason explains it better than I do...Marjan Venema

2 Answers

4
votes

The normal approach is to post a message to the control that is to be freed. See how TForm.Release is implemented for example. In fact I see no reason why you can't even re-use the CM_RELEASE message.

The point about posting the message is that is goes to the back of the queue and only gets processed once any synchronous messages (i.e. those delivered by SendMessage) have completed processing. This avoids calling methods on an object after it has been freed which is obviously an error that you are clearly well aware of.

2
votes

First, I would suggest that you write a custom control that inherits from TScrollBox, and that you provide the sub-control instantiation and removal as a feature inside that scroll box, and not something done "out there in the open" in your form. This code would go in its own unit, and only the public elements of it will be visible outside. That's just object oriented basics.

Secondly, if you are removing (deleting) controls from a scroll box, a Timer is just a source of chaos. It is possible that if you have also subclassed every control you put into that container, that you could use the mechanism used by TForm.Release(it sends them CM_RELEASE messages), and implement CM_RELEASE in a way that controls delete themselves when sent this message, however I find this ugly, except in the case of Edit controls that are destroyed when they lose focus.

I would directly delete these methods, without recourse to a timer, by subclassing both the TScrolLBox class, and any other classes that I want to put into it, and then having the removal of a control be handled by the parent object (TScrollBox), rather than by outside manipulation of any kind.