|
Developer's Guide to ASP.NET 3.5 |
|
Master ASP.NET 3.5 development using C# and Visual Studio.NET 2008. Web forms, server controls, data binding, AJAX, ASMX and WCF services and more... |
|
|
Kriya Yoga for Software Developers |
|
Importance of concentration, confidence, positive attitude, better personality and stress free life need not be emphasized separately in today's competitive world... |
|
Creating Data Bound Templated Control
Introduction
Data bound controls are most popular amongst developers because of their
verticality. Controls such as GridView and DataList are popular not just because
they provide rich features out of the box but because they allow great
deal of customization. To that end templates go a long way in providing a
customized look and feel. The concept of templates can be extended to custom
controls also. With templates in place you can customize the way your data is
presented to the user. This lesson is going to illustrate how this can be done.
Data Binding and Templates
Strictly speaking a templated control need not be always data bound. However,
in most of the real world cases data drives your presentation layer. Hence it is
worth to learn how to develop a templated control that is data bound. This means
the control will allow you to use the familiar Eval() and Bind() data binding
expressions.
In order to develop a data bound templated control you need to inherit your
custom control class from CompositeDataBoundControl base class. The
CompositeDataBoundControl class represents the base class for tabular data bound
controls (GridView for example). When you inherit your custom control class from
the CompositeDataBoundControl base class you must override the
CreateChildControls(IEnumerable, Boolean) method to create the control
hierarchy. Individual child controls can then be accessed using the Controls
collection.
If you wish to use Eval() or Bind() data binding expressions in your custom
control then you also need to create a class that implements the
IDataItemContainer interface. This interface enables data bound controls to
identify a data item object for data binding operations.
Example
In order to demonstrate what you just learnt let's develop a simple templated
control. Begin by creating a new ASP.NET web site adding a new class in its
App_Code folder. The class declaration is shown below:
namespace BinaryIntellect
{
public class MyTemplatedControl:CompositeDataBoundControl
{
...
Here we created MyTemplatedControl class within BinaryIntellect namespace.
The MyTemplatedControl class inherits from CompositeDataBoundControl base class.
The MyTemplatedControl is our main custom control class.
Now add another class named TemplateItem as shown below:
public class TemplateItem:Control,IDataItemContainer
{
...
The TemplateItem class inherits from Control base class and more importantly
it implements IDataItemContainer interface. The TemplateItem class represents an
individual data item within a template. The IDataItemContainer interface forces
the class to implement three properties viz. DataItem, DataItemIndex and
DisplayIndex.
Declare three variables inside the TemplateItem class as shown below:
private object _DataItem;
private int _DataItemIndex;
private int _DisplayIndex;
The _DataItem variable is of type object and represents individual data item
(object from a collection, DataRow etc.) that is bound with the template
control. The _DataItemIndex variable indicates the index of the data item in the
data source and the _DisplayIndex indicates position of the item as displayed in
the templated control. Remember that an item can be at 100th position inside a
data source but because of features such as paging and sorting might get
displayed at say 10th position inside the templated control.
These three variables are wrapped in three read-only properties as shown
below:
public object DataItem
{
get { return _DataItem; }
}
public int DataItemIndex
{
get { return _DataItemIndex; }
}
public int DisplayIndex
{
get { return _DisplayIndex; }
}
Since these properties are read-only implement a constructor that will accept
the values for these members.
public TemplateItem(object dataItem, int index)
{
_DataItem = dataItem;
_DataItemIndex = _DisplayIndex = index;
}
Notice that in our example we are not making any specific use of DisplayIndex
property.
Now open the MyTemplatedControl class and add the following property
declaration to it:
private ITemplate _itemtemplate;
[TemplateContainer(typeof(TemplateItem))]
public ITemplate MyItemTemplate
{
get { return _itemtemplate; }
set { _itemtemplate = value; }
}
Here, we declared a variable of type ITemplate. This variable is wrapped in a
property MyItemTemplate. The MyTemplate property indicates a template of our
custom control. The way inbuilt controls such as GridView and DetailsView define
templates such as ItemTemplate and HeaderTemplate we define MyItemTemplate
template.
Observe that the MyItemTemplate property is decorated with TemplateContainer
attribute. The TemplateContainer attribute specifies a class that is acting as a
data item for data binding purposes.
Now override the CreateChildControls() method as shown below:
protected override int CreateChildControls
(System.Collections.IEnumerable dataSource,
bool dataBinding)
{
int index=0;
if (dataBinding)
{
foreach (object dataItem in dataSource)
{
if (_itemtemplate != null)
{
TemplateItem container = new
TemplateItem(dataItem, index);
_itemtemplate.InstantiateIn(container);
Controls.Add(container);
index++;
container.DataBind();
}
}
}
return index;
}
The CreateChildControls() method accepts two parameters viz. the data source
and a Boolean value indicating if the CreateChildControls() method was called
during data binding.
inside we iterate through the data source. With each iteration we create an
instance of TemplateItem class. Remember that data source will be supplied from
web form and each element of the data source acts as the data item for the
template. Then we call InstantiateIn() method of the ITemplate object and
specify that the controls from the template will have TemplateItem object as
their container. Finally DataBind() method is called on the TemplateItem object.
Notice that the index variable simply keeps track of the item index and is
returned as a return value of CreateChildControls() method.
This completes our custom control. Let's use it on some web form. We will
bind our control with a generic collection of Employee objects. The Employee
class is shown below:
public class Employee
{
public Employee(string fname, string lname)
{
_firstname = fname;
_lastname = lname;
}
private string _firstname;
public string FirstName
{
get { return _firstname; }
set { _firstname = value; }
}
private string _lastname;
public string LastName
{
get { return _lastname; }
set { _lastname = value; }
}
}
The Employee class defines two simple properties namely Firstname and
LastName. The constructor initializes the respective variables.
Now open the default web form and add the @Register directive in its markup
file.
<%@ Register
Namespace="BinaryIntellect"
TagPrefix="cc1" %>
Then declare one instance of MyTemplatedControl as shown below:
<cc1:MyTemplatedControl runat="server" ID="control1">
<MyItemTemplate>
<h1>
<%# Eval("FirstName") %>
<%# Eval("LastName") %>
</h1>
</MyItemTemplate>
</cc1:MyTemplatedControl>
Notice the use of <MyItemTemplate> markup tag. Recollect that we created
MyItemTemplate property in MyTemplatedControl class which is of type ITemplate.
Since our custom control class inherits from CompositeDataBoundControl base
class we can use Eval() expression inside our template.
After defining the template add the following code in the Page_Load event
handler.
protected void Page_Load(object sender, EventArgs e)
{
List<Employee> data = new List<Employee>();
Employee e1 = new Employee("Nancy", "Davalio");
Employee e2 = new Employee("Andrew", "Fuller");
data.Add(e1);
data.Add(e2);
control1.DataSource = data;
control1.DataBind();
}
Here, we simply populate a generic list of Employee objects and bind our
templated control with it.
A sample run of the web form should resemble the following screen shot.
