AspAlliance.com LogoASPAlliance: Articles, reviews, and samples for .NET Developers
URL:
http://aspalliance.com/articleViewer.aspx?aId=366&pId=-1
The Basics of Templates in Server Controls
page
by Justin Lovell
Feedback
Average Rating: 
Views (Total / Last 10 Days): 33849/ 51

Introduction

First Article – The Basics of Building Server Controls
Second Article – Reusing and Creating Server Controls

Third Article - Building Composite Server Controls (previous article)

 

Download the source that is demonstrated in this article.

 

At a stage in your development of server controls, you will need to understand how templates work under server controls. Templates are not only restricted for use for server controls but can be used for any control that will appear on the page.

 

Templates are generally used in data-bounded controls. However, it would be necessary for an introduction first. This article will demonstrate:

 

  • A simple output from a server control with a template.
  • Create a template which will modify the output of any data-bounded control.

 

This article will also assume that you are familiar with the template controls like the Repeater and the DataList controls.

A Simple Example

Let’s introduce the simple code for adding controls that are in templates to the control collection:

 

[ParseChildren(true)]
[PersistChildren(false)]
public class SimpleTemplateControl : Control, INamingContainer {
   private ITemplate pSimpleTemplate;
 
   [Browsable(false)]
   [PersistenceMode(PersistenceMode.InnerProperty)]
   public ITemplate SimpleTemplate {
      get { return pSimpleTemplate; }
      set { pSimpleTemplate = value; }
   }
 
   protected override void CreateChildControls() {
      Controls.Clear();
 
      if (SimpleTemplate != null)
         SimpleTemplate.InstantiateIn(this);
   }
}

 

First, we will look at the attributes applied to the control. These attributes are required to be applied to the control class so the ASP.NET parser treats the text inside the control as part of the control; therefore, the page parser can designate the template to be “built”. These attributes are already implemented on the WebControl class so it may not be necessary for you to apply these attributes.

 

The template of the above example is the SimpleTemplate property. There is a Browsable attribute attached to the SimpleTemplate property to tell the IDE to that it may not be visible to the property explorer. The PersistanceMode attribute that is also applied to the SimpleTemplate property tells ASP.NET that the property’s contents (or template contents) can be placed as an inner property. Hence, we can get a simple mark-up for registering the control on the page.

 

<cc1:SimpleTemplateControl runat=server>
   <SimpleTemplate>
      Hi, I am inside a template. So is the text box:
      <asp:TextBox runat=server />
   </SimpleTemplate>
</cc1:SimpleTemplateControl>

 

This is where templates in server controls become a very familiar scene amongst the creation of composite server controls. Essentially, what the CreateChildControls method does now is tells the template to put the controls into the actual container control (the SimpleTemplateControl control in this case). In the CreateChildControls method, I checked if the template property was null (nothing in Visual Basic) before I called its only member (the InstantiateIn method). The reason for this is to check if a template has been built or not – by a control developer or the ASP.NET parser.

 

However, to explain the logic of the InstantiateIn method: this method is called by the container of the template. If the template has any controls to add to the container control, it has to be done over here and no where else. Basically, the templates in the containing controls are abstracts of controls but are not given life yet because it is not added to the page’s control tree – it is seen as part of the control. Once the InstantiateIn method is called, it is the templates chance to put the abstract controls that it contains into the page’s control tree; hence, giving the controls inside the template, the gift of life.

The parameter that is parsed in the InstantiateIn method is normally the container control which all the abstract controls are added to that control tree. The container control is normally the current control which has the INamingContainer interface applied… but nothing stops the container control to be another control inside the “current” server control (that is when there are “deep” composite controls).

Guidelines and Trends… and Tips

Now that we have looked at a simple example, I can declare that the same guidelines and trends apply when creating composite controls… in addition:

 

The containing control should inherit from a basic control or a template server control

 

This is a fast and hard guideline. If your control inherits from the Panel control or similar controls, then expect the functionality to break. The reason is: with the attributes that are applied to the Panel class, makes the text inside the control to be treated as child controls… not as part of the containing control (as what the previous sample demonstrated and discussed briefly on).

 

Create a property and with a value type of ITemplate interface.

 

This is to allow the other control developers to extend your template based control. Your template should be a “pluggable” property – either can be set programmatically or my mark-up: the later was demonstrated on the first page of this article and the other (the first mention) will be demonstrated later in the article. There is one exception for this trend:

 

If you need to provide a type of default template for the template property, follow the same rules as if the ITemplate was a server control.

 

There will be some times that you will want a “default” template. This situation will only occur if one is trying to programmatically set the template property. Instead of having an ITemplate interface as the property’s data type, the template should be a class.

 

This template class, that implements the ITemplate interface, should follow the same general guidelines and trends as if it is a server control. The reason for this is that you do not want to limit the extension of your server control at all (and hiding templates should not hinder the consistency at all). For example, let’s say that you had a template class:

 

public class MyTemplate : ITemplate {
   public virtual void InstantiateIn(Control container) {
      Label label = new Label();
 
      // ...
 
      container.Controls.Add(label);
   }
}

 

The “template class” should have the ability to be extended such as (just in the same fashion as with actual server controls):

 

public class MyExtendedTemplate : MyTemplate {
   public overrides void InstantiateIn(Control container) {
      Button button = new Button();
 
      // ...
 
      base.InstantiateIn(container);
      container.Controls.Add(button);
   }
}

 

The properties that expose the templates should have the getter and setter methods.

 

This is required by ASP.NET. Do not fall into this trap because this is a very common mistake made by control developers and normally find themselves dumb-founded when ASP.NET throws an error.

 

All controls created by the template, should not have an ID set

 

There may be problems if you set the ID’s of the controls inside the template, to something. Rather let ASP.NET control the ID for the controls inside the template.

 

The container control should implement the INamingContainer interface

 

Brutally said, this is to avoid complications with the previous guideline. Things can get hairy without this interface and most of the time; this will bail you out of the mess. Obviously, this “hair” will only grow if the controls inside the template actually do post backs to the server.

 

Suffix your property name with “Template” if it exposes a template

 

This is a naming convention for your properties. For example, if you had a control that you wanted to expose a template property by the name of “Foo”, then the name of the property should be “FooTemplate”. This describes what the property is and also keeps consistency with the framework (you have the ItemTemplate template in the Repeater control and so forth).

 

Always create new instances of controls in the InstantiateIn method

 

This is a trap that most developers fall into when creating “repeater” controls. This is just a guideline and the reason for people running into this trap is for the simple reason that .NET handles all variables as pointers/references instead of objects.

Modifying Templates Programmatically (Simple)

I am going to demonstrate the basics of setting templates programmatically. To do a basic demonstration, I will use the simple template example that was demonstrated at the beginning of the article. But first, let’s create the template class:

 

public class SimpleCustomTemplate : ITemplate {
   public void InstantiateIn(Control container) {
      container.Controls.Add(new LiteralControl(“Hello World from the template”));
   }
}

 

Is it simple? To do a follow through, the InstantiateIn method is called. It then adds a literal control to the container control which is parsed by the only parameter. Now to hook it up with the control provided earlier in the article:

 

public class ExtendedTemplateControl : SimpleTemplateControl {
   public ExtendedTemplateControl() {
      SimpleTemplate = new SimpleCustomTemplate();
   }
}

 

The code is really simple: all it does is set the SimpleTemplate property to the template that was defined earlier on this page (the SimpleCustomTemplate class). This, however, allows the page developer to override our template by the usual course. If you want to force your custom template, then do it at the last minute – at the CreateChildControls method:

 

public class ExtendedForcedTemplateControl : SimpleTemplateControl {
   public overrides void CreateChildControls() {
      SimpleTemplate = new SimpleCustomTemplate();
      base.CreateChildControls();
   }
}
Modifying Templates Programmatically (Advanced)

Now that we got the basics of programmatically changing the template, let’s do a rather simple approach for creating a programmatic template for the Repeater control. First, I am going to build the template class and explain the new things that made it through into this example:

 

public class CustomItemTemplate : ITemplate {
   private CustomRepeater repeater;
 
   public void InstantiateIn(Control container) {
      Label label = new Label();
      label.DataBinding += new EventHandler(LabelDataBounded);
      container.Controls.Add(label);
      container.Controls.Add(new LiteralControl("<br>"));
   }
 
   public CustomItemTemplate(CustomRepeater parentRepeater) {
      repeater = parentRepeater;
   }
 
   private void LabelDataBounded(object sender, EventArgs e) {
      Label label = (Label)sender;
 
      if ((repeater.DataItemText != null) && (repeater.DataItemText.Length != 0))
      label.Text = DataBinder.Eval(((RepeaterItem)label.NamingContainer).DataItem,
         repeater.DataItemText).ToString();
   }
}

 

On the constructor, the parameter that is parsed is to reference of the containing repeater. This will be used when the item is being data bounded (will continue later part on this page).

 

From there on, the explanation is as per normal template (covered on previous page). The only difference is that there is a control called CustomRepeater. It is quite obvious it is a custom control that derives from the Repeater control:

 

public class CustomRepeater : Repeater {
   private string pDataItemText;
 
   public string DataItemText {
      get { return pDataItemText; }
      set { pDataItemText = value; }
   }
 
   public CustomRepeater() {
      ItemTemplate = new CustomItemTemplate(this);
   }
}

 

An additional property was added and named DataItemText. This is where the reference to the CustomRepeater, in the CustomItemTemplate constructor, is vital. The property will basically record the page developer’s instruction of which field the CustomItemTemplate will use to fill the Label control that it withholds.

 

When the CustomRepeater is data-bounded, this is also the point where the Label gets data bounded too. In turn, the DataBinding event is raised on the Label control because the RepeaterItem (which will contain the template instance) is data-bounded. Internally, the RepeaterItem tells the templates to instantiate its controls into the RepeaterItem; and then the RepeaterItem tells all children controls (that was added from the template) to data bind by calling the DataBind method.

 

The DataBind method raises the DataBinding event. The sender of this event is the control that its DataBind method was called. With that said, the delegated method, the LabelDataBounded method, the sender of the control is actually the Label.

 

The LabelDataBounded method then checks if the DataItemText property, from the CustomRepeater, is not null (Nothing in Visual Basic). If it is not null, it then uses the trusty DataBinder.Eval method. The only difference over here is that the first parameter is not the normal “Container”. Instead, the first parameter points to the RepeaterItem control which contains the DataItem that the current “row” of data must bound to.

 

And the importance of the reference to the CustomRepeater is to get the column which the Label will output.
An Ending Note

That does it for the simple approach of templates for server controls. There is only two techniques for creating and setting templates both by the mark up (on the Page or User Control) or programmatically. Both of those were demonstrated in this article.

 

In the next article, I will take the templates approach and use it in a real situation – custom data binding. By creating a replicate of the Repeater control.

 

I think that I got my articles to everyone’s liking but if you have any other suggestions or constructive critics (I do welcome them as well), hit the feedback link at the top of this page. I also invite you to rate this article with the “rate this article” link at the bottom of the page (just underneath the adverts).


Product Spotlight
Product Spotlight 

©Copyright 1998-2024 ASPAlliance.com  |  Page Processed at 2024-04-26 11:58:27 PM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search