So far, we have extensively used only three standard controls: TLabel, TButton, and TEdit. In order to build more complex applications, we have to see how and when to use other controls in the Standard category.
The TCheckBox component is used to present the user with an option that can be selected or deselected. The most important property of the TCheck- Box component is the Checked property, which specifies whether or not the check box is selected. The OnClick event handler is the best place for code that needs to be executed when the state of the check box control changes.
Now, we'll see how to use the TCheckBox component to enable the user to show/hide and enable/disable all buttons on the form. First, add several TButton components to the Designer Surface. Place them anywhere you like but don't modify their properties in the Object Inspector. Now add two TCheckBox components to the Designer Surface. Set the Caption property of the first check box to "Show" and the Caption property of the second check box to "Enable." Set the Checked property of both check boxes to True. You need to set the Checked property to True because the buttons on the Designer Surface are by default both visible and enabled and the check box controls have to show their true state (see Figure 14-1).
Figure 14-1: The user interface of the TCheckBox example
There are at least three possible ways to implement this functionality. The first way is the easiest but least sophisticated, more error-prone, and time consuming. It involves modifying the Enabled and Visible properties for every button on the form.
Listing 14-1: The least sophisticated way of working with a large number of components
procedure TMainForm.ShowButtons(Sender: TObject); begin Button1.Visible := CheckBox1.Checked; Button2.Visible := CheckBox1.Checked; Button3.Visible := CheckBox1.Checked; Button4.Visible := CheckBox1.Checked; Button5.Visible := CheckBox1.Checked; Button6.Visible := CheckBox1.Checked; Button7.Visible := CheckBox1.Checked; end; procedure TMainForm.EnableButtons(Sender: TObject); begin Button1.Enabled := CheckBox2.Checked; Button2.Enabled := CheckBox2.Checked; Button3.Enabled := CheckBox2.Checked; Button4.Enabled := CheckBox2.Checked; Button5.Enabled := CheckBox2.Checked; Button6.Enabled := CheckBox2.Checked; Button7.Enabled := CheckBox2.Checked; end;
The second and much better way of working with a larger number of components includes two things: default component names and the FindComponent method. Usually, the Designer Surface generates nonintuitive component names that we have to change if we want to have easily readable code. But the ComponentName + UniqueIndex naming style for components dropped on the Designer Surface is very useful here. It gives us the ability to easily reference the needed components by calling the FindComponent method in a loop.
Here is the declaration of the FindComponent method:
function FindComponent(const AName: string): TComponent;
The FindComponent method uses the AName parameter to search through the Components property and returns the component with the matching name. If no component is found, the function returns nil. The Components property is a list of all components owned by a specific component.
The following code shows how to determine if a form owns a component named "MyButton."
Listing 14-2: Searching for a specific component
procedure TMainForm.Button1Click(Sender: TObject); begin if FindComponent('MyButton') <> nil then ShowMessage('The MyButton component exists'); end;
To show/hide or enable/disable all buttons on the form, we have to call the FindComponent method in a loop and use the loop counter to generate the needed component name. Since the FindComponent method always returns a TComponent, we have to typecast the resulting component to TButton before we can reference the Enabled and Visible properties (see Listing 14-3A for the Delphi version and Listing 14-3B for the C++ version).
Listing 14-3A: Using the FindComponent method
procedure TMainForm.ShowButtons(Sender: TObject); var Cnt: Integer; Comp: TComponent; begin for Cnt := 1 to 7 do begin Comp := FindComponent('Button' + IntToStr(Cnt)); TButton(Comp).Visible := CheckBox1.Checked; end; // for Cnt end; procedure TMainForm.EnableButtons(Sender: TObject); var Cnt: Integer; Comp: TComponent; begin for cnt := 1 to 7 do begin Comp := FindComponent('Button' + IntToStr(Cnt)); TButton(Comp).Enabled := CheckBox2.Checked; end; end;
To typecast the TComponent returned by the FindComponent in a C++Builder application, you can use either the standard bracketed typecast or the C++ static_cast typecast, which has the following syntax:
static_cast<destination_type>(original_value)
The ShowButtons() method in Listing 14-3B shows how to use the bracketed typecast, and the EnableButtons() method shows how to use static_cast to typecast a TComponent to a TButton.
Listing 14-3B: Using the FindComponent method in a C++Builder VCL application
void __fastcall TForm1::ShowButtons(TObject *Sender) { TComponent* Comp; for(int i = 1; i <= 7; i++) { Comp = FindComponent("Button" + IntToStr(i)); ((TButton*)Comp)->Visible = CheckBox1->Checked; } } void __fastcall TForm1::EnableButtons(TObject *Sender) { TComponent* Comp; for(int i = 1; i <= 7; i++) { Comp = FindComponent("Button" + IntToStr(i)); static_cast<TButton*>(Comp)->Enabled = CheckBox2->Checked; } }
The third way of working with a large number of components at run time is to browse through the Components property manually and find out if a component is of type TButton or any other component type that we need. To find out the component type at run time, we have to use the is operator. The syntax of the is operator is:
Object is Class
The is operator returns True if the object is an instance of the Class type or an instance of one of the class descendants. For example, the following test always evaluates to True because the main form is a descendant of TForm.
Listing 14-4A: Determining the object's class at run time
procedure TMainForm.Button1Click(Sender: TObject); begin if Self is TForm then ShowMessage('The main form is really a form.'); end;
To determine the object's class in a C++Builder VCL Forms application, try to typecast it using the dynamic_cast typecast (see Listing 14-4B). If the type- cast fails, it will return a NULL pointer.
Listing 14-4B: Determining the object's class in a C++Builder VCL Forms application
void __fastcall TForm1::Button1Click(TObject *Sender) { if(dynamic_cast<TForm*>(this)) ShowMessage("The main form is really a form."); }
In order to find all buttons on a form, we have to browse through the Components property of the form. The ComponentCount property specifies the number of components in the Components list. The first component in the Components list has the index 0, and the last component has the index ComponentCount – 1.
Listings 14-5A and 14-5B show how to use the is operator in Delphi and dynamic casting in C++ applications to find and update all buttons on a form.
Listing 14-5A: Using the is operator to find and update all buttons on a form
procedure TMainForm.ShowButtons(Sender: TObject); var Cnt: Integer; begin for Cnt := 0 to Pred(ComponentCount) do begin if Components[Cnt] is TButton then TButton(Components[Cnt]).Visible := CheckBox1.Checked; end; end; procedure TMainForm.EnableButtons(Sender: TObject); var Cnt: Integer; begin for Cnt := 0 to Pred(ComponentCount) do begin if Components[Cnt] is TButton then TButton(Components[Cnt]).Enabled := CheckBox2.Checked; end; end;
Figure 14-2: Using the is operator
Listing 14-5B: Using C++ dynamic casting to find and update all buttons on a form
void __fastcall TForm1::ShowButtons(TObject *Sender) { for(int i = 0; i < ComponentCount; i++) { if(dynamic_cast<TButton*>(Components[i])) ((TButton*)Components[i])->Visible = CheckBox1->Checked; } }