I was working on a solution where there was a need to render sitemap data purely using DIVs. Upon initial coding, I tried to use the standard TreeView controls such as the ASP.NET
TreeView and other third party controls. But none of them would allow for the custom rendering logic this project required. Most treeview or tree controls implement their own rendering logic that will break your design rules. Some times there is no "can't we just use what is available?", and when all else was exhausted, I just coded my way through it!!!! Nesting the
Repeater would make sense, but coding this would get ugly since it was unknown how deep each tree was. Traversing the data in the
Render method was the final solution taken. (Now try to get your HTML guy to fix the styling on that ).
After the solution was done, I decided to build a
HierarchicalRepeater control using ASP.NET 2.0's
HierarchicalDataBoundControl base class and allowing for total customization of the HTML styling and layout of hierarchical data, rather than the approach of overriding the
Render method of the control used.
So I had my objective, but where to get started?
Here is a listing of problems that I wanted to solve in building this control:
- Heck write my first CodeProject article.
- Allow for complete control of the rendering logic in templates.
- Ability to define multiple templates at each depth within a hierarchical structure.
- Allow for DataBinding expressions:
Create a hierarchical data structure using Generics that will allow a developer to define any object as a hierarchical using the
The Structure of the HierarchicalRepeater Control
I started by defining a template infrastructure that will allow for hierarchical data to be iterated:
runat="server" SiteMapProvider="MenuProvider" />
<cc1:HierarchicalRepeater runat="server" ID="repeater">
Using this structure, I was able to create the following HTML:
- Node3 1
- Node3 2
- Node3 3
- Node3 4
The core function that allows this to happen is the
protected virtual void CreateControlHierarchyRecursive(IHierarchicalEnumerable dataItems)
Hierarchical data sources rely on being able to iterate its nodes recursively. The
HierarchicalRepeater achieves this by calling
CreateControlHierarchyRecursive if there is a detection that the current data item has child items. This means that a full traversal of the data source is achievable.
But then I got to thinking, "Great, I have a
HierarchicalRepeater control, but what about custom rendering styles per node depth?" With a little help from article's by Danny Chen, I followed his solution for creating a CustomTemplate and then wrapped that into a collection for use within the
Repeater control. The
ItemTemplate control allows you to specify at which depth and which
ListItemType you would like to override. This helps when each depth has its own rendering logic, or the filtering renders specific depths when it is necessary to omit rendering of deep child nodes past a certain depth.
Hierarchical Repeater Control Using TemplateCollection
The following code renders a different color for each depth in a SiteMap. If you notice I have only one item footer template that will be used for all corresponding item templates.
<cc1:ItemTemplate Depth="0" ListItemType="ItemTemplate">
<div style="padding-left:10px;border: 1px solid blue; background: blue;">
<cc1:ItemTemplate Depth="1" ListItemType="ItemTemplate">
<div style="padding-left:10px;border: 1px solid red; background: red;">
<cc1:ItemTemplate Depth="2" ListItemType="ItemTemplate">
<div style="padding-left:10px;border: 1px solid red; background: green;">
//Default ItemFooterTemplate used for all
HierarchyData<ListItem> root =
new HierarchyData<ListItem>(new ListItem("Root", "Root"), null);
HierarchyData<ListItem> child1 =
new HierarchyData<ListItem>(new ListItem("Child1", "child1"),root);
HierarchyData<ListItem> child2 =
new HierarchyData<ListItem>(new ListItem("Child2", "child2"),root);
HierarchyData<ListItem> child2_1 =
new HierarchyData<ListItem>(new ListItem("Child2_1", "child2_1"),child2);
HierarchyDataCollection<HierarchyData<ListItem>> coll =
repeater.DataSource = coll;
Rendered output of the control:
What is that Generic HierarchyData<T> in your code you ask?
There are only a few
HierarchicalDatabound controls or classes that implement IHierarchicalEnumerable. Two notable controls are
SiteMapDatasource, and a handful of classes that are used in conjunction with both datasource controls. Of course, since I am trying to sell you my
HierarchicaRepeater control, it is only natural that I provide you with a mechanism for using your own objects to create hierarchical data, instead of you writing all the plumbing code for
IHierarchicalEnumerable. Only after using Generics heavily for collections and lists did I realize how amazing Generics are. My attempt with the
HierarchyData<T> is all the plumbing you need to start creating your own hierarchical datasources. So with
HierarchyData<T>, you are now ready to create and render complex hierarchical data sources with full control over the rendering of your data.