Iterating regions in .NET templates (TOM.NET)

SDL Tridion Sites 9 has introduced concept of predefined page regions. You can find detailed explanation on how to define region schemas and use them on the page in this webinar recording.

In this blog I would like to cover in more details how to work with regions in page templates and how to start using regions in existing implementations. This information is intended for implementers so it will be quite technical, with lots of code samples. Let's start!

First, we need to figure out how to access regions and component presentations in each region. Before Sites 9, page used to have a list of all component presentations on the page: Page.ComponentPresentations.

Page with regions is a bit more complex. It has hierarchy of the regions and each region may have other regions and component presentations. Region has such properties as RegionName, FullyQualifiedName (path to a nested region), Metadata, RegionSchema, list of nested regions, list of component presentations and so on. Note that page itself implements IRegion interface. It means that the page itself has the same new properties and you can process page and nested regions in the same way.

The Page.ComponentPresentations collection contains only component presentations that are stored directly on the page. It does not contain presentations from nested regions. If there is a region with name Main on the page, you can access its component presentations using code like this:

var mainRegion = page.Regions.First(r => r.RegionName == "Main");
foreach(var cp in mainRegion.ComponentPresentations)
{...}

Here is a helper method that can be used to flatten all component presentations on the page from all regions into a single list (C# code fragment):

<%@Import Namespace="Tridion.ContentManager.CommunicationManagement.Regions" %>
<%!
private void ProcessRegion(IRegion region, List<Tridion.ContentManager.Templating.ComponentPresentation> componentPresentations)
{
foreach (var cp in region.ComponentPresentations)
{
   componentPresentations.Add(new Tridion.ContentManager.Templating.ComponentPresentation(new TcmUri(cp.Component.Id), new TcmUri(cp.ComponentTemplate.Id)));
}
 
// Recursively process nested regions
foreach(IRegion nestedRegion in region.Regions)
{
   ProcessRegion(nestedRegion, componentPresentations);
}
}
%>

Having this helper, you can extract all component presentations into a variable and push it into the templating package:

// Get a context page
string pageUri = package.GetValue("Page.ID");
Page page = (Page) engine.GetSession().GetObject(pageUri);
// Page is also a region
IRegion pageRegion = page as IRegion;
 
// Process all component presentations in all regions (including page itself)
var allComponentPresentations = new List<Tridion.ContentManager.Templating.ComponentPresentation>();
ProcessRegion(pageRegion, allComponentPresentations);

Next time we'll talk about new default template building block that simplifies processing of component presentations on the page and regions.