Solved

How to free dynamic TImages... tricky question.

Posted on 1998-10-13
13
220 Views
Last Modified: 2010-04-06
I create 10 to 20 TImages when a certain button is pressed.  I fill in the tag of each image with a certain value.  I load a bitmap into the image.  Most importantly I assign a one predefined procedure to the OnClick of each TImage.

This predefined procedure performs some loading of stuff in the program.  As soon as the procedure is completed I want to free the TImages... I don't want them hanging around using precious resources (my program is already relatively resource hungry).  Unfortunately I can't free the TImages in the procedure that is attached to the TImages OnClick event, as it causes an AccessViolation when the procedure tries to end... since the TImages no longer exist, the fucntion no longer has the reference back to the objects it was expecting.

How do I free these TImages??  Is there any way to call a procedure from the OnClick procedure that would be guarenteed to execute after the OnClick procedure is finished?  I thought of using a TTimer, but this just seems to kludgy.  There has got to be a better way.
0
Comment
Question by:mheacock
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 5
  • 2
  • 2
  • +3
13 Comments
 
LVL 10

Expert Comment

by:Jacco
ID: 1342775
procedure TForm1.FormCreate(Sender: TObject);
begin
  FImage:=TImage.Create(Application);
  with FImage do begin
    Parent:=Self;
    Picture.LoadFromFile('c:\HotConLogo.bmp');
    OnMouseUp:=Image1MouseUp;
  end;
end;

procedure TForm1.Image1MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  TImage(Sender).Free;
end;

This way it works!

Regards Jacco
0
 
LVL 7

Expert Comment

by:BlackMan
ID: 1342776
What I would do, is to add the images to a TList when they are created and then make the OnClick procedure just send a message with the tag value. Then you can create a messagehandler, which loops thru the list, does whatever is needed and frees the image.

Something like:

Const
  mcMyMessageHandler = WM_USER + 1;

// Implemented in the Form class
     Procedure MyMessageHandler(Var Msg : TMessage); Message mcMyMessageHandler;

And then in the implementation..

Procedure TMainForm.OnImageClick(Sender : TObject);
Begin
  SendMessage(FMainForm.Handle, mcMyMessageHandler, (Sender as TImage).Tag, 0)
End;

Procedure MyMessageHandler;
[pseudo code ON]
Loop thru the list to find a object with tag=msg.wparam
Do whatever you like..
Free the image
[Pseudo code OFF]

If you want a more detailed example, just say so...

0
 
LVL 3

Author Comment

by:mheacock
ID: 1342777
I'm going to reject answers until tonight, then I will look over all answers and invite the one I like best to answer again.  I want as many people to answer this as possible.  People tend not to answer answered questions.
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 4

Expert Comment

by:erajoj
ID: 1342778
Hi,
Here is how I do it. It uses PostMessage to make sure the message sent doesn't get handled until the event handler is done.

unit Unit1;

interface

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

const
  MSG_KILLIMAGE = WM_USER;

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure ImageClick(Sender: TObject);
  private
    procedure KillImage( var msg: TMessage ); message MSG_KILLIMAGE;
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
var
  x, y: Integer;
begin
  SetCurrentDir( ExtractFileDir( ParamStr( 0 ) ) );
  for x := 0 to 3 do
    for y := 0 to 3 do
    begin
      with TImage.Create( Self ) do
      begin
        AutoSize := True;
        Left := 10 + x * 80;
        Top  := 10 + y * 80;
        Parent := Self;
        Picture.LoadFromFile( 'bitmap1.bmp' );
        OnClick := ImageClick;
      end;
    end;
end;

procedure TForm1.KillImage( var msg: TMessage );
begin
  ( TObject( msg.LParam ) as TImage ).Free; // here is where the image gets killed!
end;

procedure TForm1.ImageClick(Sender: TObject);
begin
  PostMessage( Handle, MSG_KILLIMAGE, 0, Integer( Sender ) ); // prepare to die!!!
end;

end.

Hope this helps!

/// John

0
 
LVL 8

Expert Comment

by:ZifNab
ID: 1342779
My first idea was also using a TList..., but I wasn't the first... :-)
0
 
LVL 3

Author Comment

by:mheacock
ID: 1342780
I'm going to reject answers until tonight, then I will look over all answers and invite the one I like best to answer again.  I want as many people to answer this as possible.  People tend not to answer answered questions.
0
 
LVL 3

Author Comment

by:mheacock
ID: 1342781
I was already using a TList, since there doesn't seem to be anyway to get to these components through the Component[I] property.  Setting name doesn't work and checking for TImages with large tag values doesn't work either.  There may be another way of getting dynamically created TImages into the ComponentCount list, but I don't know it.

Anyhow, I'm going to try out some of the suggestions.  I'll probably ask for an answer from someone tonight or tomorrow morning.

It is looking like Erajoj is the winner though, at the moment, since he seems to have a good implementation.   The only difference is that my KillImage proc would free all the images, thus I still need my TList to maintain references to all the TImages I create.

For those that may read this for information, I cannot stress enough the importance of TLists.  They are, for me, the most widely used and general purpose class in the VCL.  Very simple to use and very generic.  Be careful though, improper use can lead to massive memory leaks when freeing the TList and the objects stored in the TList.  Good knowledge of Delphi pointers is usually necessary, as you are usually casting dereferences and such to other type structures.

Thanks everyone.
0
 
LVL 3

Author Comment

by:mheacock
ID: 1342782
A toss up between Blackman and Erajoj.  But since Blackman gave Sendmessage as the call and Erajoj correctly gave PostMessage, I will now ask that erajoj post his answer and I shall award him an A.  (Sorry, BM, I know you posted the answer first, but Erajoj's was more correct and more complete).
0
 
LVL 20

Expert Comment

by:Madshi
ID: 1342783
I like Erajoj's answer the most. I'm using such a mechanism in my programs, too. BTW, "Form.Release" works exactly the same way...

Regards, Madshi.
0
 
LVL 7

Expert Comment

by:BlackMan
ID: 1342784
No problem with me...
0
 
LVL 10

Expert Comment

by:Jacco
ID: 1342785
My answer was simpler.

But the beauty of Delphi is that you can also do thing complex ;-)

Regards Jacco
0
 
LVL 4

Accepted Solution

by:
erajoj earned 250 total points
ID: 1342786
Hi,
Glad you think my comment was worth rewarding.
Here's an implementation of freeing all images:

unit Unit1;

interface

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

const
  MSG_KILLIMAGE = WM_USER;

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure ImageClick(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    FImageList: TList;
    procedure KillAllImages;
    procedure KillImage( var msg: TMessage ); message MSG_KILLIMAGE;
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
var
  x, y : Integer;
  Image: TImage;
begin
  SetCurrentDir( ExtractFileDir( ParamStr( 0 ) ) );
  FImageList := TList.Create;

  for x := 0 to 3 do
    for y := 0 to 3 do
    begin
      Image := TImage.Create( Self );
      FImageList.Add( Image );
      with Image do
      begin
        AutoSize := True;
        Left := 10 + x * 80;
        Top  := 10 + y * 80;
        Parent := Self;
        Picture.LoadFromFile( 'bitmap1.bmp' );
        OnClick := ImageClick;
      end;
    end;
end;

procedure TForm1.KillAllImages;
begin
  while ( FImageList.Count > 0 ) do // kill 'em all!
  begin
    TImage( FImageList[ 0 ] ).Free; // here is where the images gets killed!
    FImageList.Delete( 0 ); // remove list entry
  end;
end;

procedure TForm1.KillImage( var msg: TMessage );
begin
  KillAllImages; // structural programming ;-)
end;

procedure TForm1.ImageClick(Sender: TObject);
begin
  PostMessage( Handle, MSG_KILLIMAGE, 0, 0 { Integer( Sender ) } ); // prepare to die!!!
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  KillAllImages;   // destroy images
  FImageList.Free; // destroy list
end;

end.

/// John
0
 
LVL 3

Author Comment

by:mheacock
ID: 1342787
You didn't have to post a TList implementation.  I already did that myself.  Might be useful for other people, but seeing as there is no "search past answers" functionality, well, I doubt anyone actually looks at any past answers.

To Jacco... your version my work, but it is still sloppy in my opinion (and shouldn't work) as you are still freeing the TImages from a method that is hooked into the TImages (via the OnMouseUp).
0

Featured Post

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!

Question has a verified solution.

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

A lot of questions regard threads in Delphi.   One of the more specific questions is how to show progress of the thread.   Updating a progressbar from inside a thread is a mistake. A solution to this would be to send a synchronized message to the…
The uses clause is one of those things that just tends to grow and grow. Most of the time this is in the main form, as it's from this form that all others are called. If you have a big application (including many forms), the uses clause in the in…
If you're a developer or IT admin, you’re probably tasked with managing multiple websites, servers, applications, and levels of security on a daily basis. While this can be extremely time consuming, it can also be frustrating when systems aren't wor…
Monitoring a network: how to monitor network services and why? Michael Kulchisky, MCSE, MCSA, MCP, VTSP, VSP, CCSP outlines the philosophy behind service monitoring and why a handshake validation is critical in network monitoring. Software utilized …

717 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