Writing New Controls

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 of Existing Controls

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.

Composition by Writing Templates

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.

<com:TLabel ID="Label" ForControl="TextBox" /> <com:TTextBox ID="TextBox" />

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,

class LabeledTextBox extends TTemplateControl { public function getLabel() { $this->ensureChildControls(); return $this->getRegisteredObject('Label'); } }

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.

Composition by Overriding createChildControls()

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,

class LabeledTextBox extends TControl implements INamingContainer { private $_label; private $_textbox; protected function createChildControls() { $this->_label=new TLabel; $this->_label->setID('Label'); // add the label as a child of LabeledTextBox $this->getControls()->add($label); $this->_textbox=new TTextBox; $this->_textbox->setID('TextBox'); // add the textbox as a child of LabeledTextBox $this->getControls()->add($textbox); } public function getLabel() { $this->ensureChildControls(); return $this->_label; } public function getTextBox() { $this->ensureChildControls(); return $this->_textbox; } }

Using LabeledTextBox

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,

<com:LabeledTextBox ID="Input" Label.Text="Username" />

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

Extending existing controls is the same as conventional class inheritance. It allows developers to customize existing control classes by overriding their methods or providing new functionalities. The difficulty of the task depends on how much an existing class needs to be customized.

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.

Extending TControl

TControl is the base class of all control classes. It implements the following properties and methods that are commonly used in derived control classes,

  • ID - a string uniquely identifying the control among all controls of the same naming container. An automatic ID will be generated if the ID property is not set explicitly.
  • UnqiueID - a fully qualified ID uniquely identifying the control among all controls on the current page hierarchy. It can be used to locate a control in the page hierarchy by calling TControl::findControl() method. User input controls often use it as the value of the name attribute of the HTML input element.
  • ClientID - similar to UniqueID, except that it is mainly used for presentation and is commonly used as HTML element id attribute value. Do not rely on the explicit format of ClientID.
  • Enabled - whether this control is enabled. Note, in some cases, if one of the control's ancestor controls is disabled, the control should also be treated as disabled, even if its Enabled property is true.
  • Parent - parent control of this control. The parent control is in charge of whether to render this control and where to place the rendered result.
  • Page - the page containing this control.
  • Controls - collection of all child controls, including static texts between them. It can be used like an array, as it implements Traversable interface. To add a child to the control, simply insert it into the Controls collection at appropriate position.
  • Attributes - collection of custom attributes. This is useful for allowing users to specify attributes of the output HTML elements that are not covered by control properties.
  • getViewState() and setViewState() - these methods are commonly used for defining properties that are stored in viewstate.
  • addParsedObject() - this method is invoked for each component or text string enclosed within the component tag specifying the control in a template. By default, the enclosed components and text strings are added into the Controls collection of the control.
  • createdOnTemplate() - this method is invoked when the control is created on a template. By default, it calls the enclosing control's addParsedObject() method.
  • createChildControls - this method is invoked by ensureChildControls().
  • saveState() and loadState() -
  • render() - this method renders the control. By default, it renders items in the Controls collection.
  • Control lifecycles -

Extending TWebControl

getTagName() addAttributesToRender renderBeginTag renderContents renderEndTag

PostBackHandler PostBackEvnetHandler DataBoundControl