EPiServer PageTree with ItemDataBound event

Tinkering around with a small EPiServer tool plugin that I am building (will be found in a later post), I decided that I wanted to try out the PageTree control; I have not been using it that much, and wanted to know what it could offer me. Nice, easy to use, and lots of work already done. Sweet. What it did not give me however, was access to an ItemDataBound event with useful EventArgs, such as for instance the Repeater with it’s RepeaterEventArgs. After a few minutes of Reflectoring, I decided to have a go at extending it a little. Source code for this can be found at the bottom of this post.

PageTree.cs

public class PageTreeEventArgs : EventArgs
{
    public Control Item{ get; set; }
    public PageData DataItem { get; set; }
}

[Serializable]
public delegate void PageTreeEventHandler(object sender, PageTreeEventArgs e);

The first thing that we need for this is an event handler (15) and the actual arguments class (8). Make the PageTreeEventArgs extend EventArgs, and add something to hold the PageData object as well as the template container.

public class PageTree : EPiServer.Web.WebControls.PageTree
{
    public event PageTreeEventHandler ItemDataBound;

    protected override void CreateChildControls()
    {

In the new PageTree class, start by adding the new ItemDataBound event (19) allowing us to actually attach something to it. The following parts are a bit of copy-paste from the EPiServer source, courtesy of Resharper. It is not great, I know, but you cannot override every method in the world. The CreateChildControls() is where the magic happens. Almost, anyway. It makes a call to a private recursive method CreateItemsRecursive(..) which in turn, calls another private method, CreateItemTemplateControl(..), for every page in the data source; this is where we place a small addition.

private void CreateItemTemplateControl(PageData page, int level, bool hasChildren, PageReference currentPageLink)
{
    PageTemplateContainer template = new PageTemplateContainer(page, level, hasChildren);
if (ItemDataBound != null)
{
    var pageTreeEventArgs = new PageTreeEventArgs
    {
        Item = template,
        DataItem = page
    };
    ItemDataBound.Invoke(this, pageTreeEventArgs);
}
this.Controls.Add(template);

At the far end of this method, just before the template is added to the control collection (99), check whether or not someone has attached a handler to our new event. If they have, assemble the event arguments and fire the event handler (92, 97).

Using the PageTree ItemDataBound event

To use the new event in your code you must first register the control. Add a line specifying the proper namespace and assembly of where you put your new class at the top of your code-in-front. Since I was using the EPiServer AlloyTech templates I point to the EPiServer.Templates.AlloyTech DLL and get a namespace called EPiServer.SampleCode.

UsageSample.aspx

<%@ Register TagPrefix="SampleCode" Namespace="EPiServer.SampleCode" Assembly="EPiServer.Templates.AlloyTech" %>

The actual control looks the same as the original EPiServer one, except for the TagPrefix not being EPiServer, and of course the OnItemDataBound attribute (17). Frederik Vig made an informative post, if you would like to know more about how to use it.

<SampleCode:PageTree
    ID="pageTreeWithItemDataBound"
    OnItemDataBound="pageTreeWithItemDataBound_OnItemDataBound"
    runat="server">
</SampleCode:PageTree>

There is nothing special about the code-behind, just add your event handler as you would in the usual scenario. You can now, for instance, use the e.DataItem property which we created earlier to access the currently processed PageData object, or use the template container to find other controls.

UsageSample.aspx.cs

protected void pageTreeWithItemDataBound_OnItemDataBound(object sender, PageTreeEventArgs e)
{
    var pageName = e.DataItem.PageName;
    var myLabel = e.Item.FindControl("myLabelId") as Label;
    //Or do something useful.
}

Source code

Note: The usage examples in the post are for the initial version 1.0.0.0. The code was moved to a separate Visual Studio project in version 1.1.0.0, so you need to use the new namespace and assembly from this version forward.

Source code: PageTree.zip (Initial version 1.0.0.0)
Source code: PageTreeExtended_src_v1.1.0.0.zip (Latest)
Droppable blinary: PageTreeExtended_binary_1.1.0.0.zip (Latest)
GitHub-link: https://github.com/matkun/Blog/tree/master/PageTreeExtended