Creating a Composite Control in Asp .Net

A choice towards a composite control is made when there is a need for a group of controls to perform a particular action in sync.

Also it is going to be used in many projects. For example if we want a calculator UI to be made, then it needs a text box, a number of button controls to be grouped together along with the functional code. If this calculator UI and functionality are going to be reused in many projects, then it is wiser to create it as a re-usable control, instead of copy/pasting the code every time. This way, when different web controls are put together as a single control, it makes a composite control.

Adding the Composite Control to the Visual Studio .Net:

A composite control is a Group of some UI of existing controls along with the functional code compiled into a web control library dll . These composite controls need to be used by adding them to the Tool box of Visual Studio .Net. They can be added to the Visual Studio .Net tool box as follows.

  1. Expand the components group and Right click on the toolbox.
  2. Click the Add/Remove items.
  3. Browse to the dll path and ensure that it is checked from the list. It is checked by default when we choose the dll.
  4. Click Ok.

This will add the composite control to the toolbox. After this, the control can be used normally like any other controls like TextBox, Button etc., Just a drag and drop will do the required actions. When we add this control, VS .Net will add the reference to dll in the background automatically, add the custom server control to the page, add a variable to the code behind file etc.,

Creating a Composite Control:

A Composite Control is derived from the class System.Web.UI.Control.
It also needs to implement the INamingContainer interface which can create the controls with a unique id. INamingContainer interface achieves this by creating a unique namespace for the Custom server control placed on an aspx page.

Many of the web controls like Repeater, DataGrid etc., which support databinding implement this INamingContainer interface. This is because, these controls internally create a lot of other controls on the fly. This needs uniqueness for the controls as they also allow access to these inner controls.

Our goal of this exercise is to create a composite control as a simple calculator with two text boxes, one dropdownlist, a button and a label control to show the result. This will give us a simplistic view of the whole stuff.

To Create a Composite control, first create a Web Control Library Project under Visual C# Projects. This will create a default WebCustomControl1.cs file, which can be used for creating a control from scratch. But our case is to create a composite control. We can still use this file. Just rename the file and class name to MyCompositeControl.cs.

The next job is to make sure that the class inherits from the Control class and implement the INamingContainer interface. The wizard would have already put in the base class as System.Web.UI.WebControls.WebControl for us. This class is derived from System.Web.UI.Control. Now our job is to add INamingContainer in addition to this. The intellisense in VS .Net is so intuitive that, when typing in this INamingContainer it will prompt us to press the tab key to include the members to be implemented. But as INamingContainer does not have any members, this is not necessary.

Also the VS .Net would have added a function inside the class definition, which will be used while creating a control from scratch. This function has to be deleted now. If this function is kept, then the resultant page will try to display the code from this function and it’ll confuse the programmers.

The CreateChildControls function should also be overridden. When we type protected override VS .Net automatically lists all the overridable member functions and we can choose our CreateChildControls function. Add the following code for this function.

///<summary>
/// This CreateChildControls should be used to write code for
/// creating our composite controls
///</summary>

protected override void CreateChildControls()
{
TextBox text1 = new TextBox();
TextBox text2 = new< TextBox();
DropDownList ddlist1 = new DropDownList();
Button cmdResult = new Button();
Label lblResult = new Label();

text1.Height = Unit.Pixel(25);
text1.Width = Unit.Pixel(70);
text1.Text = "0";

text2.Height = Unit.Pixel(25);
text2.Width = Unit.Pixel(70);
text2.Text = "0";

ddlist1.Height = Unit.Pixel(25);
ddlist1.Width = Unit.Pixel(40);

ddlist1.Items.Add("+");
ddlist1.Items.Add("-");
ddlist1.Items.Add("*");
ddlist1.Items.Add("/");

cmdResult.Height = Unit.Pixel(25);
cmdResult.Width = Unit.Pixel(50);
cmdResult.Text = "Result";

lblResult.Height= Unit.Pixel(25);
lblResult.Width = Unit.Pixel(200);
lblResult.Text = "Result Will be displayed here";

LiteralControl lc = new LiteralControl("<br>");

cmdResult.Click +=new EventHandler(cmdResult_Click);

Controls.Add(text1);
Controls.Add(ddlist1);
Controls.Add(text2);
Controls.Add(cmdResult);
Controls.Add(lc);
Controls.Add(lblResult);
}

private void cmdResult_Click(object sender, EventArgs e)
{
Label lblResult = (Label)Controls[5];
try
{

TextBox text1 = (TextBox)Controls[0];
DropDownList ddBox = (DropDownList)Controls[1];
TextBox text2 = (TextBox)Controls[2];
this.EnsureChildControls();

switch(ddBox.SelectedValue)
{

case "+":
lblResult.Text = Convert.ToString(Convert.ToInt32(text1.Text)+Convert.ToInt32(text2.Text));
break;

case "-":
lblResult.Text = Convert.ToString(Convert.ToInt32(text1.Text)-Convert.ToInt32(text2.Text));
   break;

case "*":
lblResult.Text = Convert.ToString(Convert.ToInt32(text1.Text)*Convert.ToInt32(text2.Text));
   break;

case "/":
lblResult.Text = Convert.ToString(Convert.ToInt32(text1.Text)/Convert.ToInt32(text2.Text));
   break;

}

}
catch(Exception eVal)
{

lblResult.Text = eVal.ToString();

}
}

One other useful point to be noted is, when we add the Event Handler code for the button for the cmdResult.Click, the intellisense again prompts us to press the <TAB> to create the handler code automatically. Once tab is pressed, it creates our function signatures automatically and we can go in and type our logic inside the handler.

The above code takes input of the numbers and displays the result of the selected arithmetic operation. It does not do any advanced error handling except displaying the Exception message.

The above controls will not be visible on  the Visual Studio .Net designer, as this needs a designer class to be created for the composite control.

Adding the Necessary Designer Code:

To display the control in the design view, we need to create a separate Design viewer class and specify the designer HTML needed.
The Designer class should be derived from System.Web.UI.ControlDesigner and the GetDesignTimeHtml function should be overridden.

To do this, add a new class MyCompositeControlDesigner into the project and add the following code in the MyCompositeControl.CS file.

[Designer("MySampleControls.MyCompositeControlDesigner,MySampleControls")]
public class MyCompositeControl : System.Web.UI.WebControls.WebControl, INamingContainer
{

}

The Designer class level attribute makes sure that the Control MyCompositeControl has its designer view code in the class MyCompositeControlDesigner under the namespace MySampleControls. The actual designer code has to be written as follows. The following code goes into the overridden GetDesignTimeHtml function which is a member of MyCompositeControlDesigner.

public override string GetDesignTimeHtml()
{
StringWriter sw = new StringWriter();
HtmlTextWriter hw = new HtmlTextWriter(sw);

TextBox text1 = new TextBox();
TextBox text2 = new TextBox();
DropDownList ddlist1 = new DropDownList();
Button cmdResult = new Button();
Label lblResult = new Label();

text1.Height = Unit.Pixel(25);
text1.Width = Unit.Pixel(70);
text1.Text = "0";

text2.Height = Unit.Pixel(25);
text2.Width = Unit.Pixel(70);
text2.Text = "0";

ddlist1.Height = Unit.Pixel(25);
ddlist1.Width = Unit.Pixel(40);
ddlist1.Items.Add("+");
ddlist1.Items.Add("-");
ddlist1.Items.Add("*");
ddlist1.Items.Add("/");

cmdResult.Height = Unit.Pixel(25);
cmdResult.Width = Unit.Pixel(50);
cmdResult.Text = "Result";

lblResult.Height= Unit.Pixel(25);
lblResult.Width = Unit.Pixel(200);
lblResult.Text = "Result Will be displayed here";

LiteralControl lc = new LiteralControl("<br><br>");

text1.RenderControl(hw);
ddlist1.RenderControl(hw);
text2.RenderControl(hw);
cmdResult.RenderControl(hw);
lc.RenderControl(hw);
lblResult.RenderControl(hw);

return sw.ToString();
}

The GetDesignTimeHtml function now renders the controls in Visual Studio .Net as they will look when the application is running.

The project can be built now. This will create a dll as MySampleControls.dll. The control MyCompositeControl under this MySampleControls.dll can be used by referring the dll in the project.

Using the Composite Control:

The composite control created above can now be used in any web application by referring to the MySampleControls.dll. The following lines explain how to use this composite control.

  • Create a new Web Application.
  • Open an existing webform and right click on the Toolbox –>Components.
  • Click Add/Remove and browse for the MySampleControls.dll.
  • Clicking Ok will add the control “MyCompositeControl” on the toolbox.
  • Drag ‘n’ drop the MyCompositeControl into the web form.
  • Now this will run like a full fledged simple calculator page.