RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Web Control Templates Explained : Page 3

Programming for extensibility assures ease of maintainability in your design. Control templates offer this functionality to custom Web controls.

Adding to the Control Hierarchy
Now it's time to insert these template properties into the EmailContact control's control hierarchy. This control hierarchy includes all the child controls that make up the EmailContact control, including the heading and the "send" button.

The first thing I have to do is decide logically where I want these templates displayed. I did that already when I decided that I wanted the HeaderTemplate to display directly below the e-mail form's heading, and the FooterTemplate directly above the "send" button. But now I also have to decide where this will happen, physically in the code.

As I taught in previous articles, composite controls get their control hierarchy built in a method override called CreateChildControls. There, all the child controls get added to the Controls collection property as well as some literal text, typically in the form of HTML table code used to position the child controls. In the case of the EmailContact control, I built an HTML table in this method and inserted all the child controls in between table row tags. Here's an example of what this looks like:

   With Me.Controls
      .Add(New LiteralControl("<table>"))
      .Add(New LiteralControl("<tr><td>"))
      .Add(New LiteralControl("</td></tr>"))
      .Add(New LiteralControl("<tr><td>"))
      .Add(New LiteralControl("</td></tr>"))
      .Add(New LiteralControl("<tr><td>"))
      .Add(New LiteralControl("</td></tr>"))
      .Add(New LiteralControl("</table>"))
   End With
So inserting my template properties will literally mean adding a couple of new rows to the table. However, you'll recall that I declared the template properties as type ITemplate and did not instantiate them. I told you that when the page developer uses my templates, they will get instantiated. I need to account for this not taking place by checking the template properties for null values. If they are not null (nothing), it means that the page developer has used them and I can add their contents to the control hierarchy. If the properties evaluate as null then the template does not contain anything on the form and I don't need to do anything with it.

So a condition check is the first thing I have to do before I add anything to the control hierarchy. Once this condition passes, I then have to create an instance of the container control I created earlier, TemplateItem. You've been taught in the past that a composite control's Controls collection contains all the control's children. These children are other Web controls, so the need for the TemplateItem container control is becoming more obvious.

Let's take a look at the code that inserts the HeaderTemplate into the EmailContact control's control hierarchy, and then I'll continue explaining it. I'm including some of the surrounding code so you get an idea of where I am inserting it.

   .Add(New LiteralControl("</td></tr>"))
   If _HeaderTemplate IsNot Nothing Then
      Dim headerTemplateItem As TemplateItem = _
         New TemplateItem
      headerTemplateItem.ID = "headerTemplateItem"
      _HeaderTemplate.InstantiateIn( _
      .Add(New LiteralControl("<tr><td>"))
      .Add(New LiteralControl("</td></tr>"))
   End If
   .Add(New LiteralControl("<tr><td>"))
Notice that first I'm creating an instance of the container control and setting its ID property. This is a common practice with any composite control's children which I taught you in my previous articles. The next thing you see is where the hook up happens between the container and the template property. Remember I told you that the ITemplate interface defines the InstantiateIn method and here is where I use it.

   _HeaderTemplate.InstantiateIn( _
This line of code takes the HeaderTemplate property, along with all its contents (whatever the page developer added to it), and instantiates it all inside the container control instance. Now that I have an instance of an actual Web control, headerTemplateItem, I treat it like any other Web control that I want to build into a control's children hierarchy. So I do this by simply adding it to the Controls collection like everything else; and like everything else I use HTML table elements to correctly position it.

So not to bore you too much, I'll save you the agony of watching me do exactly the same thing for the FooterTemplate. The only difference would be the name of the object I use to instantiate the TemplateItem control; I would use one named footerTemplateItem.

Now you know how to create and add templates to custom Web controls; at least in the most basic way. I still have some more to talk about but first let me show you how to use what you've created so far.

Using Templates from the Page
Remember this code?

   <dnd:EmailContact2 ID="EmailContact1"
It's what the control will render on the ASPX page when you drop it on the form. I can add any other Web controls I want between any of the two template tags; remember these represent the two template properties. So I'm going to do just that. I'll add a checkbox and textbox to the header template and a link button to the footer template.

Figure 2: The EmailContact control (with template contents).
     <asp:CheckBox ID="CheckBox1" runat="server" />
     <asp:TextBox ID="TextBox1" 
     <asp:LinkButton ID="LinkButton1" 
When I switch to design view, you'll see something like Figure 2.

So now you see that the page developer (me in this case) has added controls to my control completely unbeknownst to me. OK, visually that's all fine and dandy, but the page developer has to be able to do something with these, right? I'm glad you agree, so next I'm going to teach you how to access these controls from the page's code-behind page so they can become functional.

Accessing Template Contents
Template content is just like a Web control's child controls, in the way that they cannot be "directly" accessed from the hosting page's code-behind page. By "directly" I mean as easy as saying:

   TextBox1.Text = "Miguel"
Remember, a control can only directly access its immediate Controls collection. From the code in my EmailContact control, I can access all the children I put in there. From the code-behind of a form, I can only directly access the EmailContact control because it resides in the Controls collection of the form (a form is a control too). Anything deeper than that requires a bit more work and templates are no exception. To dig into a Web control's internal children, you would normally need to use the FindControl method and access them by their ID. I'll start there but remember that the child control I am looking for is the template's container control. So in the code-behind class for a form, I can use code like this:

   Dim container As TemplateItem = _
      DirectCast(EmailContact1.FindControl( _
      "headerTemplateItem"), TemplateItem)
This will give me access to the header template container, so (using the same technique) I can dive deeper and obtain its contents—the controls the page developer added:

   Dim txt1 As TextBox = _
      DirectCast(container.FindControl( _
      "TextBox1"), TextBox)
Now I can use the visual power of the templates and their behavioral power as well by accessing their contents. However, there is one piece of behavior that you may also need. If a developer drops a link or a button into one of the templates, they need to be able to trap its Click event, right? I created the templates with no knowledge of what contents may go into them so I have to account for everything and that includes event activity.

You may be familiar with an event in the GridView called ItemCommand. This event receives click actions from links and buttons that a page developer may drop into a grid's template columns. Now I'm going to show you exactly how this works.

Close Icon
Thanks for your registration, follow us on our social networks to keep up-to-date