Control Arrays
While the concept of a control array
doesn't exist as such in Delphi, the functionality (and much more)
is certainly available to you. There are two reasons to create
a control array in Visual Basic. The first is so that multiple
controls might share the same code. This can be accomplished by
simply assigning the same event handler to say the OnClick event
handler of multiple components on a Delphi form. The second is
that you want to create controls on the fly. This too is more
than possible in Delphi.
You might, for example, create a
form with three buttons. In Visual Basic, they would all have
the name B and different indexes. You could then write code like
this:
Sub B_Click (Index As Integer)
Caption = B(i).Caption
End Sub
If you want to accomplish the same
thing in Delphi, you would create three buttons and give them
descriptive names such as B_1, B_2 and B_3. You would then create
an event handler for one of them called B and then assign that
event handler to the other two. That event handler would look
something like this:
procedure
TForm1.BClick(Sender:TObject);
begin
Caption := TButton(Sender).Caption;
end;
It should now be clear the purpose
of that "Sender" parameter that's been ignored up until
now. It refers to the component which activated the event handler.
Just as you use the Index parameter in Visual Basic to differentiate
between the controls which might call the event handler, you use
Sender to determine the source of the event in Delphi.
The second thing of note is that
you must "cast" the Sender parameter to a particular
object type. The data type of Sender is simply TObject
which could be just about anything in Delphi. Therefore, a button,
a text box and a check box could all share the same event handler
in Delphi, something which would not be possible in Visual Basic.
The only price for this flexibility is that you must tell the
compiler how to treat this TObject by casting it to an
object type a little lower down in the hierarchy. The syntax for
casting is much like that of a function where you simply use the
class name as the function and the TObject as the single
parameter. This tells Delphi to temporarily treat this TObject
as a TButton. You would need to continue to cast this variable
as you needed it. If you wanted to make your code a little more
readable, you could make an assignment to a local variable of
type TButton like this:
procedure
TForm1.BClick (Sender:TObject);
var
Source:TButton;
begin
Source := TButton(Sender)
Caption := Source.Caption;
end;
Dynamic Control Arrays
The other reason to use a control
array in Visual Basic is that you want the ability to create controls
"on the fly" based on run-time information. The mechanism
in VB is that you must create a template and then build copies
of that template. You have no such limitations in Delphi. You
can create any object on the fly and dynamically assign event
handlers to it.
In the VB sample, ARRAY.FRM form
in the vbsamplescontrolscontrols.mak, there is an option button
on the form called optButton with an index of 0 which gets used
as a template in the cmdAdd_Click procedure:
Sub cmdAdd_Click ()
If MaxId = 0 Then MaxId
= 1
If MaxId > 8 Then Exit
Sub
MaxId = MaxId + 1
Load OptButton(MaxId)
OptButton(MaxId).Top =
OptButton(MaxId - 1).Top + 400
OptButton(MaxId).Visible
= True ' Display new button.
OptButton(MaxId).Caption
= "Option" & MaxId + 1
End Sub
A comparable routine in Delphi might
look something like this:
procedure
TForm1.cmdAddClick(Sender: TObject);
var
rbOld, rbNew: TRadioButton;
begin
if nCtl = 0 then
nCtl := 1
else
Inc(nCtl);
if nCtl < 16
then begin
rbOld := TRadioButton(FindComponent
('optButton_' + IntToStr (nCtl -1)));
rbNew := TRadioButton.Create(Self);
rbNew.Parent := Self;
rbNew.SetBounds (rbOld.Left,
rbOld.Top + rbOld.Height * 2,
rbOld.Width,
rbOld.Height);
rbNew.Caption := 'Option'
+ IntToStr(nCtl);
rbNew.Name := 'optButton_'
+ IntToStr(nCtl);
rbNew.OnClick := optButton_0Click;
end;
end;
In this code the beginning looks
just like the Visual Basic example, checking for valid data. Then
the following occurs:
1 Set
rbOld to last component created.
This is accomplished with a very
useful function called FindComponent which takes a string value.
Try that in VB!
1 rbNew
is created
2 rbNew
is placed on the form
3 rbNew
is moved to a new location
4 rbNew's
caption is set
5 rbNew's
name is set!
6 rbNew's
OnClick handler is dynamically assigned to the optButton_0Click
handler!
The net result is exactly the same
as with the Visual Basic code above. The optButton_Click
handler in VB looks like this:
Sub optButton_Click (Index
As Integer)
picDisplay.BackColor =
QBColor(Index + 1)
End Sub
and looks very similar in Delphi:
procedure
TForm1.optButton_0Click(Sender: TObject);
var
i:Integer;
rb:TRadioButton;
begin
rb := TRadioButton(Sender);
Caption := rb.Name;
i := StrToInt (Copy (rb.Name,
11, 2));
shpDisplay.Brush.Color
:= QBColor (i);
end;
The big difference is that in the
absence of an Index parameter, you're extracting the last
character of the caption to get the index value. This is just
one technique to handle control arrays. For example, the Tag
property in Delphi is a long integer rather than a string. So
the two routines could look like this:
procedure
TForm1.cmdAddClick(Sender: TObject);
var
rbOld, rbNew: TRadioButton;
begin
if nCtl = 0 then
nCtl := 1
else
Inc(nCtl);
if nCtl < 16
then begin
rbOld := TRadioButton(FindComponent
('optButton_' + IntToStr (nCtl -1)));
rbNew := TRadioButton.Create(Self);
rbNew.Parent := Self;
rbNew.SetBounds (rbOld.Left,
rbOld.Top + rbOld.Height * 2,
rbOld.Width,
rbOld.Height);
rbNew.Caption := 'Option'
+ IntToStr(nCtl);
rbNew.Name := 'optButton_'
+ IntToStr(nCtl);
rbNew.Tag := QBColor (nCtl);
{this is new}
rbNew.OnClick := optButton_0Click;
end;
end;
procedure
TForm1.optButton_0Click(Sender: TObject);
begin
shpDisplay.Brush.Color
:= TRadionButton(Sender).Tag;
end;
In the example above, the actual
color value is stored in the Tag property so that all the optButton_0Click
routine needs to do is set the color equal to the Tag of the Sender.
Finally, the elements of the control
array are removed in a manner similar to that in Visual Basic:
Sub cmdDelete_Click ()
If MaxId = 1 Then Exit
Sub ' Keep first two buttons.
Unload OptButton(MaxId)
' Delete last button.
MaxId = MaxId - 1
' Decrement button count.
OptButton(0).SetFocus
' Reset button selection.
End Sub
becomes this in Delphi:
procedure
TForm1.cmdDeleteClick(Sender: TObject);
var
rb:TRadioButton;
begin
if nCtl > 0
then
begin
rb := TRadioButton
(FindComponent ('optButton_' + IntToStr(nCtl)));
rb.Destroy;
Dec(nCtl);
end;
end;
Another example of control arrays
can be found in the CALC.DPR sample in the [VBSAMPL] directory.
Object Variables
Object variables are a rather new
concept to Visual Basic although you have seen them as parameters
to procedures in both VB and Delphi. Another way to manipulate
a set of controls as an array in Visual Basic is to create an
array of type control and populate it at Form_Load like so:
Dim T (1 To 10) As TextBox
Sub Form_Load ()
Dim c As Integer
Dim i As Integer
i = 1
For c = 0 to Form1.Controls.Count
-1
If TypeOf Form1.Controls
(c) is TextBox then
Set T(i) = Form1.Controls(c)
i = i + 1
end if
Next
End Sub
You could then walk through this
array and set a property of all of the member controls to a certain
value, just as with a control array. The same example in Delphi
might look something like this:
var
i, c:Integer;
begin
i := 1;
for c := 0 to form1.componentcount
-1 do
if Form1.Components[c]
Is TEdit then
begin
T[i] := TEdit(Form1.Components[c]);
Inc (i);
end;
end;
Object variables are also used in
Visual Basic to create multiple instances of forms using the New
keyword. The Visual Basic sample has code that looks like the
following:
Sub cmdNextInstance_Click
()
Dim NextInstance As New
frmMain
NextFormNum = NextFormNum
+ 1
NextInstance.Caption =
"Instance # " & NextFormNum
NextInstance.Left = Left
+ (Width 10)
NextInstance.Top = Top
+ (Height 10)
NextInstance.Show
End Sub
Similar functionality can be achieved
in Delphi with a procedure that looks like this:
procedure
TfrmMain.cmdNewInstanceClick(Sender: TObject);
var
F:TfrmMain;
begin
F := TfrmMain.Create(Application);
Inc(NextFormNum);
F.Caption := 'Instance
#' + IntToStr(NextFormNum);
F.Left := Left + (Width
div 10);
F.Top := Top + (Height
div 10);
F.Visible := True;
end;
With the exception of the .Create
method, this code is practically identical to the code you are
used to in Visual Basic.
Connect with Us