Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Chris Miller on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Class creation problem

Status
Not open for further replies.

wizzor

Programmer
Aug 29, 2006
23
FI
Hi all,
I have a slight problem when attempting to create a class. I'm creating it, based on TPanel, and it is supposed to include a button showing the text hello world.

Code:
unit spanel;

interface

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

type
  TSpanel = class(TPanel)
    Buttoni: TButton;
    procedure ButtoniClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Spanel1: TSpanel;

implementation

{$R *.dfm}

procedure TSpanel.ButtoniClick(Sender: TObject);
begin
Showmessage('HelloWorld');
end;

end.




There were a couple of problems however: The first, being the less major one. Delphi didn't create a dfm file for this new unit of mine, and I knew no way of making one, so I ended up copying the one for my main form, renaming it, and editing with notepad. Anyone got a clue on how this could be avoided?

Then, secondly, I tried to use this class of mine, so I tried the following code on my main form:

Code:
procedure TForm1.FormCreate(Sender: TObject);
var
s: TSpanel;

begin
s := TSpanel.Create(Self);

With Form1 do
begin
s.parent := form1;
s.SetBounds(1,1,500,500);
s.Buttoni.SetBounds(10,10,10,10) ;


The program crashes however, resulting in the following error: Project luokka2 raised an exception class EAccesViolation with message 'Access violation at address 00455ED9 in module 'luokka2.exe'. Read of address 00000000'. Process stopped. Use step or Run to continue.
 
Hi my friend.... You have to create the button runtime like this:

(This code is untested, errors may occur...)
Code:
unit spanel;

interface

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

type
  TSpanel = class(TCustomPanel)
  private
    Buttoni: TButton;
    procedure ButtoniClick(Sender: TObject);
  public
    constructor Create(AOwner: TComponent); override; 
    destructor Destroy; override;
  end;

var
  Spanel1: TSpanel;

implementation

{$R *.dfm}

constructor TSpanel.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);

  Buttoni := TButton.Create(nil);
  Button1.Left := 100;
  Button1.Top := 100;
  Button1.OnClick := ButtoniClick;
  Button1.Parent := self;
end;

destructor TSpanel.Destroy; override;
begin
  Button1.Free;
  inherited Destroy;
end;

procedure TSpanel.ButtoniClick(Sender: TObject);
begin
  Showmessage('HelloWorld');
end;

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-"There is always another way to solve it, but I prefer my way.
 
Thanks man, that worked, after some tweaking.

The following includes the corrected version. Changes are marked as comments and appear red
(This is just to help others who might find the code useful)
You also had some mixup in the button variable name, that part is not coloured.

Code:
unit spanel;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls,  ExtCtrls;
[COLOR=blue]// The 
type
  TSpanel = class(TCustomPanel)
  private
    Buttoni: TButton;
    procedure ButtoniClick(Sender: TObject);
  public
    constructor Create(AOwner: TComponent); 
    destructor Destroy; override;
  end;

var
  Spanel1: TSpanel;

implementation

{$R *.dfm}

constructor TSpanel.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);

  Buttoni := TButton.Create(nil);
  Buttoni.Left := 100;
  Buttoni.Top := 100;
  Buttoni.OnClick := ButtoniClick;
  Buttoni.Parent := self;
end;

destructor TSpanel.Destroy; [COLOR=red]//Word "override;" removed. resulted in "Unknown directive" error [/color]
begin
  Buttoni.Free;
  inherited Destroy;
end;

procedure TSpanel.ButtoniClick(Sender: TObject);
begin
  Showmessage('HelloWorld');
end;

end.

Could you also explain a bit about the changes you made? I understand that you created the button dynamically, but why are the destructor and constructor definitions necesssary? I can't see them used, when for extample, a button is created on a form dynamically.

You have helped me a lot, I've been stuck in a bigger project of mine for months, this should help me go on.

If it's ever finished, I'll add you on a special thanks list in the about box ;)
 
You can actually get rid of the destructor as long as you pass the panel as the Owner of the button on creation e.g.
Code:
constructor TSpanel.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);

  Buttoni := TButton.Create([COLOR=red]Self[/color]);
  Buttoni.Left := 100;
  Buttoni.Top := 100;
  Buttoni.OnClick := ButtoniClick;
  Buttoni.Parent := self;
end;
This ensures that TSpanel is responsible for automatically freeing the memory allocated to the TButton. Nordlund's suggestion is perfectly valid however - it depends on your needs. But if the button is to generally live on the panel all the time then I would go with making TSpanel the owner.

See the Delphi help file for more info on Owner as opposed to Parent. It's helpful to understand the distinction.

You may find the following FAQ entitled "How do I write a simple Delphi Component?" helpful: faq102-4839

The constructor is automatically called when the TSpanel is instantiated. So, if you wish to write any custom code for when the TSpanel is created, you need to override the constructor as above.

Likewise the destructor is automatically called when the object is freed.

Delphi didn't create a dfm file for this new unit of mine, and I knew no way of making one...
Delphi allows you to create a new form or create a new unit for a project. A form has an accompanying unit. A unit has no accompanying form.

Clive
Runner_1Revised.gif

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
"To err is human, but to really foul things up you need a computer." (Paul Ehrlich)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To get the best answers from this forum see: faq102-5096
 
Hmm, I was able to redo these steps in a larger project of mine, and it all actually works (I'm amazed)

How does one destroy dynamically the dynamically created object?

CPanel.Free; results in nothing that I can see,
CPanel.Destroy; Results in crash...
 
To destroy a dynamically created object use:
Code:
CPanel.Free;
or
Code:
FreeAndNil(CPanel);
...assuming of course that you didn't assign an Owner to the panel when you created it. If so, the Owner will free the dynamically created object.

Calling either of the above methods executes the destructor so you don't and shouldn't call the Destroy method explicitly. The Delphi help file will explain this in more detail.


Clive
Runner_1Revised.gif

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
"To err is human, but to really foul things up you need a computer." (Paul Ehrlich)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To get the best answers from this forum see: faq102-5096
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top