I am trying to dynamically add button controls to an application. The application I'm working on has up to 6 different pre-defined button positions which can be any one of several different operations. For example, if the user presses the "New" button, I want to dynamically show a "Save", "Clear" and "Cancel" buttons and have each of the OnClick
events point to the proper procedure/function. Each of the buttons are defined in an array of TButton
and assigned as needed to the pre-defined buttons on the form.
This is a sample program I built to help understand what I'm trying to achieve:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
Vcl.StdCtrls;
type
TForm1 = class(TForm)
btnPos1: TButton;
procedure btnNewClick(Sender: TObject);
procedure Form1Close(Sender: TObject; var Action: TCloseAction);
procedure Form1Create(Sender: TObject);
private
{ Private declarations }
public
procedure LoadButtons;
end;
var
Form1: TForm1;
FormBtns: array[1..20] of TButton;
implementation
{$R *.dfm}
procedure TForm1.LoadButtons;
begin
FormBtns[1].Caption:='&New';
FormBtns[1].OnClick:=btnNewClick;
FormBtns[1].Left:=340;
FormBtns[1].Top:=20;
btnPos1.Caption:=FormBtns[1].Caption;
btnPos1.OnClick:=FormBtns[1].OnClick;
btnPos1.Left:=FormBtns[1].Left;
btnPos1.Top:=FormBtns[1].Top;
end;
procedure TForm1.btnNewClick(Sender: TObject);
begin
// do something
end;
procedure TForm1.Form1Close(Sender: TObject; var Action: TCloseAction);
begin
Application.Terminate();
end;
procedure TForm1.Form1Create(Sender: TObject);
begin
LoadButtons;
end;
end.
When this test and actual application runs I receive the following error:
Project BtnTest.exe raised exception class $C0000005 with message 'c0000005 ACCESS_VIOLATION'.
It is occurring on the following code:
FormBtns[1].OnClick:=btnNewClick;
I am assuming that it can't reconcile the btnNewClick
reference for some reason but I don't understand why. I'm not even sure if this is best way to get the job done. Any help would be greatly appreciated. Thanks.
You have declared an array of TButton
object pointers, but you have not pointed them at anything yet. You have to actually create the TButton
objects before you can access their properties, eg:
procedure TForm1.LoadButtons;
begin
FormBtns[1] := TButton.Create(Self);
FormBtns[1].Parent := Self;
...
end;
FormBtns
should also be a member of your TForm1
class instead of a global variable.