Introduction
Have you ever wondered why ASP.NET did not come with a repeater control that
can group results? Well, I did and I decided to try to write one myself.
I have seen other solutions to this problem, but not as simple and elegant as
this one, and that is why I decided to share it.
Background
The Repeater
control that comes with ASP.NET has one very cool
event: the ItemCreated
event. This event is fired whenever an
element (or an instance of a template) is added to the Controls
collection of the control.
Furthermore, the source demos in the ASP.NET SDK showed me how to use
templates and data binding. Because it is documented in a lot of places on the
web, I'm not going to repeat that here. Just hit Google with 'Templated Databound Control ASP.NET' and you'll
see enough results.
The third part of the solution included the ability to provide a custom
Comparer
delegate that will compare 2 items of an unknown type.
The solution
What I did was the following:
- Create a subclass of the
System.Web.UI.WebControls.Repeater
class.
- Trap the
ItemCreated
event.
- Add a
GroupTemplate
template to the control (in addition to the
ItemTemplate
).
- For each item created, compare it to the previous item using a custom
Comparer
.
- If it is different, instantiate the template and databind it using the same
ItemData
as the current Item
.
- Override the
CreateChildControls
method to reset the 'last
record' member.
Anyway, I think this is a very good example of subclassing ASP.NET controls
and I'm still wondering why this one isn't included in the standard
framework.
Here's the code. It is also included in the demo solution file (see
above).
using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Collections;
using System.ComponentModel;
namespace GroupedRepeater.Controls
{
public class GroupingRepeater : System.Web.UI.WebControls.Repeater
{
private ITemplate _groupTemplate = null;
private IComparer _comparer = null;
private static object lastvalue = null;
public GroupingRepeater()
{
this.ItemCreated += new
RepeaterItemEventHandler(GroupingRepeater_ItemCreated);
}
protected override void AddParsedSubObject(object obj)
{
base.AddParsedSubObject (obj);
}
public IComparer Comparer
{
get { return _comparer; }
set { _comparer = value; }
}
[TemplateContainer(typeof(GroupHeader))]
public ITemplate GroupTemplate
{
get
{
return _groupTemplate;
}
set
{
_groupTemplate = value;
}
}
protected override void CreateChildControls()
{
lastvalue = null;
base.CreateChildControls ();
}
private void GroupingRepeater_ItemCreated(object sender,
RepeaterItemEventArgs e)
{
System.Diagnostics.Trace.WriteLine(e.Item.GetType().Name);
if(e.Item.ItemType == ListItemType.Item ||
e.Item.ItemType == ListItemType.AlternatingItem)
{
if(e.Item.DataItem != null)
{
if(_comparer.Compare(lastvalue, e.Item.DataItem) != 0)
{
GroupHeader item = new GroupHeader();
_groupTemplate.InstantiateIn(item);
item.DataItem = e.Item.DataItem;
this.Controls.Add(item);
item.DataBind();
}
}
lastvalue = e.Item.DataItem;
}
}
public class GroupHeader : Control,INamingContainer
{
private object _dataItem;
public virtual object DataItem
{
get
{
return _dataItem;
}
set
{
_dataItem = value;
}
}
}
}
}
That's it! I hope you find this useful.