Web developers wiki ASP.NET Sitecore Sharepoint Kentico by Evident Interactive

Table of Contents [Hide/Show]


Edit

Problem

Retrieve and display the descendants of an item in the order they appear in the sitecore back-end.

Suppose we have a tree:
R
|- A
|  |- A.A
|  |- A.B
|
|- B
|- C

We need to retrieve and display them as: A, A.A, A.B, B, C

Edit

Solution

My first bet was to use Item.Axes.GetDescendants(). This method returns an array of Item objects. Unfortunately this array is ordered breadth-first:

R.Axes.GetDescendants() returns: A, B, C, A.A, A.B

This makes sense when you are searching through a tree and expect to find items on a certain level, or closest to the root (R) item, or whatever, but it doesn't solve our problem.

My solution was to create an extension method on Sitecore.Data.Items.Item named GetDescendants(). Arguably, an extension method on Sitecore.Data.Items.ItemAxes named GetDescendantsDepthFirst() might be better to avoid confusion. Since I did not want to retrieve the entire tree by recursing through the items and their children, which might be expensive, I decided to use yield to return an IEnumerable<Item>.

Edit

Stack-based variant

public static IEnumerable<Item> GetDescendants(this Item thisItem)
{
	Stack<Item> items = new Stack<Item>(thisItem.Children.Reverse());

	while (items.Count > 0)
	{
		Item item = items.Pop();
		yield return item;

		if (item.HasChildren)
		{
			// Push the children in reverse order to maintain their order when popping them
			foreach (Item child in item.Children.Reverse())
			{
				items.Push(child);
			}
		}
	}
}

First, a stack is made from the children of the current Item (thisItem), in reverse order. This is done, so that they are popped in the correct order. Then we iterate through all of them, yielding them as requested by the generated iterator (see: yield (C# reference), for more information about the yield statement). Then, when an item has children, they are pushed on the stack (again in reverse order) so the next iteration will yield the first child. When the item doesn't have children, the next iteration yields the next sibling, if any.

Edit

Recursion-based variant

A version that uses recursion and still yields results (iterator style) is also possible:
public static IEnumerable<Item> GetDescendants(this Item thisItem)
{
	foreach (Item child in thisItem.Children)
	{
		yield return child;

		if (child.HasChildren)
		{
			foreach (Item i in GetDescendants(child))
			{
				yield return i;
			}
		}
	}
}

The compiler will now use the generated iterator and call this recursively. This is arguably more elegant, but also more 'dangerous' when you call this on a large tree. The difference is that where the first variant uses a Stack object that is located on the heap, the second variant places the generated iterater objects on the stack, which has a more limited size (but usually enough).

I decided to go with the first variant, but I didn't do any performance or memory profiling. YMMV.

Enjoy!

 © Evident Interactive BV