[Okta Webinar] Learn how to a build a cloud-first strategyRegister Now

x
?
Solved

Can a Button Delete Itself?

Posted on 2004-11-20
6
Medium Priority
?
236 Views
Last Modified: 2010-04-05
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?
0
Comment
Question by:MartinC
6 Comments
 
LVL 31

Expert Comment

by:moorhouselondon
ID: 12633808
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
 
LVL 1

Expert Comment

by:wavget
ID: 12634652
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
 

Author Comment

by:MartinC
ID: 12637727
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
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 31

Expert Comment

by:moorhouselondon
ID: 12638012
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
 
LVL 26

Accepted Solution

by:
Russell Libby earned 240 total points
ID: 12639485

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
 

Author Comment

by:MartinC
ID: 12716055
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

Featured Post

New feature and membership benefit!

New feature! Upgrade and increase expert visibility of your issues with Priority Questions.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Have you ever had your Delphi form/application just hanging while waiting for data to load? This is the article to read if you want to learn some things about adding threads for data loading in the background. First, I'll setup a general applica…
Hello everybody This Article will show you how to validate number with TEdit control, What's the TEdit control? TEdit is a standard Windows edit control on a form, it allows to user to write, read and copy/paste single line of text. Usua…
this video summaries big data hadoop online training demo (http://onlineitguru.com/big-data-hadoop-online-training-placement.html) , and covers basics in big data hadoop .
We’ve all felt that sense of false security before—locking down external access to a database or component and feeling like we’ve done all we need to do to secure company data. But that feeling is fleeting. Attacks these days can happen in many w…
Suggested Courses
Course of the Month19 days, 5 hours left to enroll

834 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question