tags:

views:

220

answers:

5

Hi,I have to create an array and place all controls there in order to access them.Here's a short example:

unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    const Test:Array[0..2] of TButton = (Button1,Button2,Button3);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

end.

Undeclarated idenitifier 'Button1' at the line where I declarated my array.But it's declarated three lines above.

Where's the problem,how to put all controls in an array?

EDIT:

Thank you for your answers,but I've got problems:

 var TestA:TObjectList<TButton>;

 var index:TComponent;
 begin
 TestA := TObjectList<TButton>.Create(false);
   for index in Form7 do
     if pos(index.name, 'Button') = 1 then
       TestA.add(TButton(index));

 TestA[0].Caption := 'Test'; //Exception out of range.
+3  A: 

You may not be able to reference public properties of your form in an array constant like that. Try doing it in your form constructor/OnCreate event instead.

procedure TForm1.FormCreate(Sender: TObject);
begin
  Test[0] := Button1;
  Test[1] := Button2;
  Test[2] := Button3;
end;
Ben Daniel
Please suggest another way.I have 110 images,really it will look ugly.Isn't it possible in another way?
John
Okay, I would only suggest what Mason or Lachlan have already suggested. You may want to check out Delphi Help on TControl.Controls, TControl.ControlCount, TComponent.Components, TComponent.ComponentCount, TComponent.FindComponent. These are all very useful methods for iterating over controls/components sitting on a TWinControl/owned by a component.
Ben Daniel
All our answers would work fine for your question. Could I suggest elaborating on your situation a bit so we could come up with a better suited answer?
Ben Daniel
I've got 120 Timages named 'Slot0...Slot119'. I'd like to create an array or another container so I can change their pictures easier.
John
Okay, I've posted separate answer with a recursive add to list method you might find useful too.
Ben Daniel
+2  A: 

Ben's right. You can't set up a control array in the form designer. But if you have 110 images, for this specific case you can put them into a TImageList component and treat its collection of images as an array.

If you've got a bunch of more normal controls, like buttons, you'll have to create an array and load them into it in code. There are two ways to do this. The simple way, for small arrays at least, is Ben's answer. For large control sets, or ones that change frequently, (where your design is not finished, for example,) as long as you make sure to give them all serial names (Button1, Button2, Button3...), you can try something like this:

var
  index: TComponent;
  list: TObjectList;
begin
  list := TObjectList.Create(false); //DO NOT take ownership
  for index in frmMyForm do
    if pos('Button', index.name) = 1 then
      list.add(index);
   //do more stuff once the list is built
end;

(Use a TObjectList<TComponent>, or something even more specific, if you're using D2009.) Build the list, based on the code above, then write a sorting function callback that will sort them based on name and use it to sort the list, and you've got your "array."

Mason Wheeler
Check my code in my question,I've got problems.
John
The error was in your code actually.first param is pos() is substr,not full string.Great answer,therefore accepted.All other answers +1,Thanks everyone!
John
Ack! Sorry about that. I hacked that up off the top of my head, and I can never keep the string functions straight. Some of them take substr first and some take the full string first. Fixed it now.
Mason Wheeler
+1  A: 

How about this?

procedure TForm1.FormCreate(Sender: TObject);
begin
  for b := 1 to 110 do
    Test[b] := FindComponent('Button' + IntToStr(b)) as TButton;
end;

You'll have to declare the array as a variable rather than a constant and it will have to go from 1 to 110 rather than 0 to 109 but that's no problem.

LachlanG
Bad idea, since FindComponent does an O(N) search through (potentially) the entire component set every time. Depending on how complex the form is, this could add noticeable to the setup time for the form. Better to do it in a single pass.
Mason Wheeler
Hey bad idea is a bit strong! ;-) It's clear, simple and would execute in the blink of an eye for 99% of forms. Better to write clear and simple code first then use a profiler to identify the bottlenecks before you start optimising IMO.
LachlanG
Sorry. I didn't mean to offend with a comment like that. I just figure it's better to write an algorithm that can scale. At work, I often run into bugs caused by old code that was written "simply" and probably worked just fine back when our app was a whole lot smaller than it is today, but now it's broken. Sometimes I can fix it. Other times, architecture has grown up around it and it's a real pain to adjust without breaking something else. Better to design to avoid that, IMO.
Mason Wheeler
No offense taken.
LachlanG
+2  A: 

This function will iterate over all the controls on a specified container, like a particular TPanel or even the entire form, and populate a specified TObjectList with your TImage controls.

procedure TForm1.AddImageControlsToList(AParent: TWinControl; AList: TObjectList; Recursive: boolean);
var 
  Index: integer;
  AChild: TControl;
begin
  for Index := 0 to AParent.ControlCount - 1 do
  begin
    AChild := AParent.Controls[Index];
    if AChild is TImage then // Or whatever test you want to use
      AList.Add(AChild)
    else if Recursive and (AChild is TWinControl) then
      AddImageControlsToList(TWinControl(AChild), AList, True);
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  // Call like this or similar to get your list of images
  // (assumes MyImageList is declared in Form)
  MyImageList := TObjectList.Create(False);
  AddImageControlsToList(Self, MyImageList, True);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  // Destroy the list
  FreeAndNil(MyImageList);
end;
Ben Daniel
A: 

I use this all the time - it is simple and fast (despite Mr Wheeler's comment)- declare the maxbuttons as a constant

var Form1: TForm1; pbutton:array[1..maxbuttons] of ^tbutton;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);

(* Exit *)

var k:integer;

begin

for k:=1 to maxbuttons do dispose(pbutton[k]);

close;

end;

procedure TForm1.FormActivate(Sender: TObject);

var k:integer;

begin

(*note the buttons must be Button1, Button2 etc in sequence or you need to allocate them manually eg pbutton[1]^:=exitbtn etc *)

for k:=1 to maxbuttons do

begin

new(pbutton[k]);

pbutton[k]^:= tbutton(FindComponent('Button'+IntToStr(k)));

end;

end;

procedure TForm1.ButtonMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);

var k:integer; b:boolean;

begin b:=false; k:=1;

while (k<= maxbuttons) and (not b) do

begin

if pbutton[k]^ = sender then (Note sender indicates which button has been clicked)

begin

  { found it so do something}

  b:=true;

end;

k:=k+1;

end;

end;

Why are you using ^ operator? You should use .Create and .Free methods! The are much more safer and easier to maintain, and the performane is comparable.
smok1