Can a Button Delete Itself?

I have a button that has been created on the fly, which when clicked, executes a
procedure (say UpdateContent) which changes the content of a database then
destroys the button itself, then recalls the procedure (say LoadPage)
that created the button in the first place. It works OK except that it
brings up an error message whenever the code is run (AFTER the destroy
and recall functions of LoadPage have been executed) when the
line-by-line handling returns back to UpdateContent for the final end.
This is presumably because the button that called the procedure no
longer exists. Is there any simple way around this?
MartinCAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

moorhouselondonCommented:
If in the button click procedure you make a call to the OnClick event of a hidden button on the form, then I reckon that, within the hidden button procedure you will be able to delete the original button.  You can easily identify which button Sent the click by using Tag.
0
wavgetCommented:
When you make the call to the OnClick event of the other button the program will start execution of that code which then deletes the first button. When the OnClick is completed however, it will return to the place where it was called from... the OnClick of the button you just deleted :)

One way to do this is to use PostMessage to post a mouse click message...

PostMessage(SecondButton.Handle,WM_LBUTTONDOWN,0,0);
PostMessage(SecondButton.Handle,WM_LBUTTONUP,0,0);

That way the OnCLick of the first button will be completed first after which the onClick of the 2nd button will be executed.

Hope this helps,

D2.
0
MartinCAuthor Commented:
morhouselondon: I'm afraid your solution just passes the buck one level higher. I tried a similar method at first, having the button's OnClick call a separate procedure entirely, but as wavget pointed out, eventually the code returns to the calling procedure just to do the "end" line, whereupon it falls over its own feet.

wavget: that solution looks kinda ugly ... my own ugly solution was to have the button do UpdateContent then just set a boolean called bCleanup to true, without calling the LoadPage. Then I'd use the timer to sweep through every second looking for bCleanup = true, do the destroy and recall functions of LoadPage, and reset bCleanup to false. I have the timer on there already anyway as the application has a running clock in it, so there won't be much additional overhead resource-wise. But it is not a pretty solution ... can lead to problems later. Similarly, I'd rather not muck about with the actual comp's handling of the clicks if I can help it.

Anyone got a more elegant solution?
0
Cloud Class® Course: Certified Penetration Testing

This CPTE Certified Penetration Testing Engineer course covers everything you need to know about becoming a Certified Penetration Testing Engineer. Career Path: Professional roles include Ethical Hackers, Security Consultants, System Administrators, and Chief Security Officers.

moorhouselondonCommented:
Hmmm I dynamically delete panels on a form, but thinking about it I'm doing that from a popup menu, which is outside the scope of "pulling the rug from under oneself".

I suppose my suggestion of creating a hidden button could have been simplified to saying: why not just Hide the original button and reuse it when necessary?  That depends on whether there is the possibility that multiple buttons will be created which need to be hidden/deleted.

The idea of having a timed cleanup sweep sounds good.  
0
Russell LibbySoftware Engineer, Advisory Commented:

The problem you are having is due to ignoring the warnings that delphi gives us in regards to the use of Free. To quote the help file:

Warning:      Never explicitly free a component within one of its own event handlers or free a component from the event handler of a component it owns or contains. For example, don’t free a button in its OnClick event handler or free the form that owns the button from the button's OnClick event.

Ignoring this will cause errors that are difficult to trace, or even worse, may not cause an immediate error, but will raise problems later on.

Garbage collecting a component/object through timer polling is one way to get around this. Another would be to postmessage back to the form itself. An example of this is as follows:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

// Define a window message to use for internal handling
const
  WM_FREEOBJECT     =  WM_USER + 400;

type
  TForm1            =  class(TForm)
     Button1:       TButton;
     procedure      Button1Click(Sender: TObject);
  private
     // Private declarations
  protected
     // Protected declarations
     procedure      WMFreeObject(var Message: TMessage); message WM_FREEOBJECT;
  public
     // Public declarations
     procedure      DelayedFreeObject(Obj: TObject);
  end;

var
  Form1:            TForm1;

implementation
{$R *.DFM}

// Procedure to actually free objects
procedure TForm1.WMFreeObject(var Message: TMessage);
var  Obj:        TObject;
begin
  // Check wParam
  if ((Message.wParam) <> 0) then
  begin
     // Access as TObject
     Obj:=TObject(Message.wParam);
     // Make sure Obj <> Self
     if (Obj <> Self) then Obj.Free;
  end;
end;

// Simple helper function to post message to the form, which will then handle the freeing of objects
procedure TForm1.DelayedFreeObject(Obj: TObject);
begin
  // Post free object message to form
  PostMessage(Handle, WM_FREEOBJECT, Integer(Obj), 0);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  // Delayed free of the object - exchange this with code that calls Button1.Free
  // to see the difference.
  DelayedFreeObject(Button1);
  // Call a property of the button just to test
  if Button1.Enabled then beep;
end;

end.

----

After the event handling is done, the form will go back to its message processing, at which time the WM_FREEOBJECT message will be processed and handled. This provides a very simple, and relatively clean method of freeing an object from within its own event handler. I say relatively, due to the fact that the calling of

Application.ProcessMessages

can thow a monkey wrench into this, as it tells the application to handle any messages that are sitting in the message queue (one of which would be the WM_FREEOBJECT). I can tell you that the use of TTimer would also suffer from the same problem caused by Application.ProcessMessages, as it would allow the possibility for the ttimer to free an object while the  object is in its event handler.

Anyways, regardless of the actual mechanism you use you cannot safely free a component/object from its own event handler. And if using a delayed mechanism to perform the free, then you need to make sure that you don't process the message queue while the object is in its event handler.

Hope this helps,
Russell



0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
MartinCAuthor Commented:
Hmmm, sorry, I forgot I had left this question open, so I'll close it off now. I suspect my strategy was flawed from the outset and certainly the solutions presented above seem rather open to things going wrong later. So I have bitten the bullet and coded the database handling and the screen reconstruction to occur separately i.e. I never recall the LoadPage procedure that created the button in the first place, I just duplicate in the UpdateContent procedure some of the LoadPage handing code for screen generation that I had hoped to store in a single procedure.  

Of the solutions above, rllibby's provided a comprehensive synopsis of the insoluble aspects of the problem which led me to make the decision to abandon the plan, so points to him. Thanks to others as well.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Delphi

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.