You might have noticed that I did not mention to create composition server controls from the CreateChildControls method. There is no strict rule or enforcement with this because of the “templated” composition that may occur (we discussed it on the last line. However, we are going to look at an example with the CreateChildControls method:
The example will be based on a search control. The control will contain two controls – a TextBox control and a Button control. We are going to do the simple UI and simple functionality first before we will add bells and whistles.
public class InteractiveSearchBox : Table, INamingContainer {
protected TextBox SearchText = new TextBox();
protected Button SearchButton = new Button();
protected override HtmlTextWriterTag TagKey {
get { return HtmlTextWriterTag.Table; }
}
protected override void CreateChildControls() {
TableRow row = new TableRow();
TableCell textBoxCell = new TableCell();
TableCell buttonCell = new TableCell();
buttonCell.Width = Unit.Pixel(1);
SearchTextBox.Width = Unit.Percentage(100);
textBoxCell.Controls.Add(SearchTextBox);
buttonCell.Controls.Add(SearchButton);
row.Cells.Add(textBoxCell);
row.Cells.Add(buttonCell);
Rows.Add(row);
}
protected override void OnInit(EventArgs e) {
SearchTextBox.ID = "searchText";
SearchButton.ID = "searchButton";
SearchButton.Text = "Search";
base.OnInit(e);
EnsureChildControls();
}
public InteractiveSearchBox() {
Width = Unit.Percentage(100);
}
}
Ah – running the code will output a table with a width of 100%, by default. The width can be changed from the Width property on the server control that is composing the controls. The table cells are then placed into one row; which the tow then added to the TableRowCollection which is accessible from the Rows property. But what is distinct about table cells is that it contains a text box (which will fill most of the area) and a search button.
One other essential thing that was added was the INamingContainer interface. This will be required when the same type of control becomes siblings to each other. For example, if I have search boxed for manufacturers and clients in the same PlaceHolder control, the names of the text boxes and the buttons will conflict (ASP.NET will throw an error). One interface, with no members, is what is required to make the control into a container namespace for its child controls - avoiding this conflict of names.
We are going to add two more functional features before we can declare this as a search box control. Those two features are:
Lets do the rest of the code:
public event EventHandler Search;
public string SearchText {
get { return SearchTextBox.Text; }
}
protected virtual void OnSearch(EventArgs e) {
if (Search != null)
Search(this, e);
}
That code was pretty standard (it was highlighted before in the article series). We will now do our own firing for our own event by listening to the button's Click event. We will have to do one modification to the constructor to hook up the other “unknown method”:
private void SearchButton_Click(object sender, EventArgs e) {
// call our custom event
OnSearch(e);
}
public InteractiveSearchBox() {
// the other code over here
SearchButton.Click += new EventHandler(SearchButton_Click);
}
All of this code can be accomplished with “templating” the output as well; however, complications will occur with sibling controls. For this reason, “templating” is used to give a common output – as shown on the next page.