Writing new controls is often desired by advanced programmers, because they want to reuse the code that they write for dealing with complex presentation and user interactions.
In general, there are two ways of writing new controls: composition of existing controls and extending existing controls. They all require that the new control inherit from TControl or its child classes.
Composition is the easiest way of creating new controls. It mainly involves instantiating existing controls, configuring them and making them the constituent components. The properties of the constituent components are exposed through subproperties.
One can compose a new control in two ways. One is to override the TControl::createChildControls() method. The other is to extend TTemplateControl (or its child classes) and write a control template. The latter is easier to use and can organize the layout constituent compoents more intuitively, while the former is more efficient because it does not require parsing of the template.
As an example, we show how to create a labeled textbox called LabeledTextBox using the above two approaches. A labeled textbox displays a label besides a textbox. We want reuse the PRADO provided TLabel and TTextBox to accomplish this task.
We need two files: a control class file named LabeledTextBox.php and a control template file named LabeledTextBox.tpl. Both must reside under the same directory.
Like creating a PRADO page, we can easily write down the content in the control template file.
The above template specifies a TLabel control named Label and a TTextBox control named TextBox. We would to expose these two controls. This can be done by defining a property for each control in the LabeledTextBox class file. For example, we can define a Label property as follows,
In the above, the method call to ensureChildControls() ensures that both the label and the textbox controls are created (from template) when the Label property is accessed. The TextBox property can be implemented similarly.
For a composite control as simple as LabeledTextBox, it is better to create it by extending TControl and overriding the createChildControls() method, because it does not use templates and thus saves template parsing time. Note, the new control class must implement the INamingContainer interface to ensure the global uniqueness of the ID of its constituent controls.
Complete code for LabeledTextBox is shown as follows,
To use LabeledTextBox control, first we need to include the corresponding class file. Then in a page template, we can write lines like the following,
In the above, Label.Text is a subproperty of LabeledTextBox, which refers to the Text property of the Label property. For other details of using LabeledTextBox, see the above online examples.
Extending existing controls is the same as conventional class inheritance. It allows developers to customize existing control classes by overriding their properties, methods, events, or creating new ones.
The difficulty of the task depends on how much an existing class needs to be customized. For example, a simple task could be to customize TLabel control, so that it displays a red label by default. This would merely involves setting the ForeColor property to "red" in the constructor. A difficult task would be to create controls that provide completely innovative functionalities. Usually, this requires the new controls extend from "low level" control classes, such as TControl or TWebControl.
In this section, we mainly introduce the base control classes TControl and TWebControl, showing how they can be customized. We also introduce how to write controls with specific functionalities, such as loading post data, raising post data and databinding with data source.
TControl is the base class of all control classes. Two methods are of the most importance for derived control classes:
TWebControl is mainly used as a base class for controls representing HTML elements. It provides a set of properties that are common among HTML elements. It breaks the TControl::render() into the following methods that are more suitable for rendering an HTML element:
When rendering the openning HTML tag, TWebControl calls getTagName() to obtain the tag name. Derived classes may override this method to render different tag names.
If a control wants to respond to client-side events and translate them into server side events (called postback events), such as TButton, it has to implement the IPostBackEventHandler interface.
If a control wants to be able to load post data, such as TTextBox, it has to implement the IPostBackDataHandler interface.
If a control wants to get data from some external data source, it can extend TDataBoundControl. TDataBoundControl implements the basic properties and methods that are needed for populating data via databinding. In fact, controls like TListControl, TRepeater are TDataGrid are all derived from it.