Per Magne Skuseth
Aug 27, 2013
  14828
(9 votes)

Letting your blocks know where they are

I’ve been fiddling around with how I could get information from the current content area when in the context of a block controller.
First of all, I wanted to know which tag was currently set in the content area. That was reachable through this:

   1: var tag = ControllerContext.ParentActionViewContext.ViewData["Tag"] as string;

 

Then, I tried getting the actual content area, which I could retrieve by doing this:

   1: var currentContentArea = ControllerContext.ParentActionViewContext.ViewData.Model as ContentArea;


By getting the current content area, I could get information on how many items it contained, and what those items were. I could also get the current index of the item. Knowing this, I could control which view to render, based on the current index. In the example below I’ve done just that, with the case being that the blocks rendered first, second and third in the list should have special views.


The block type that is being used:

   1: [ContentType(GUID = "81D97CE8-AF88-41FB-B4FF-BFF5D53DDDDF",
   2:     DisplayName = "Football player block")]
   3: public class PlayerBlock : BlockData
   4: {
   5:       public virtual string Heading { get; set; }
   6:       [UIHint(UIHint.Image)]
   7:       public virtual Url Image { get; set; }
   8:       public virtual XhtmlString MainBody { get; set; }
   9:  
  10:       // This property will indicate the block’s position and will be set in the controller,
  11:       // which is why I've used the [Ignore] attribute. By doing this, EPiServer won't register it
  12:       // as an editable property.
  13:       [Ignore]
  14:       public int Index { get; set; }  
  15: }



In the PlayerBlock controller, I return a view based on the current block’s index in the content area:

   1: public override ActionResult Index(PlayerBlock currentBlock)
   2: {
   3:     string blockView;
   4:     // get the current content area
   5:     var currentContentArea = 
   6:         ControllerContext.ParentActionViewContext.ViewData.Model as ContentArea;
   7:  
   8:     int index = 1;
   9:     // we'll need to check if it is actually rendered in a ContentArea.
  10:     if (currentContentArea != null)
  11:     {
  12:         // the index of the current selected block
  13:         index = currentContentArea.Contents.IndexOf(currentBlock as IContent) + 1;        
  14:     }
  15:     switch (index)
  16:     {
  17:         case 1:
  18:             blockView = "first";
  19:             break;
  20:         case 2:
  21:         case 3:
  22:             blockView = "twoandthree";
  23:             break;
  24:         default:
  25:             blockView = "index";
  26:             break;
  27:     }
  28:     currentBlock.Index = index;
  29:  
  30:     // Example:if index is 2, return view "/Views/Blocks/PlayerBlock/TwoAndThree.cshtml".
  31:     // If in preview mode, 
  32:     // the 'first' view will be return as we set the index initial value to '1'.
  33:     return PartialView(string.Format("~/Views/Blocks/{0}/{1}.cshtml",
  34:         currentBlock.GetOriginalType().Name,
  35:         blockView),
  36:         currentBlock);
  37: }


The code along with the inline comments should be quite self explanatory, but to sum up: The first item in the content area will now be rendered using the First.cshtml view, while item two and three will be rendered using the TwoAndThree.cshtml view (I guess I could have found a better name for that one), while the rest of the views will be rendered with the Index.cshtml view.

top 7 footballers

 

And finally: Keep in mind that while I’ve used the currentBlock object in all three views, I could just as well create view models, and populated them only with the data needed in the view. Performance wise, that would probably be a good idea.

Aug 27, 2013

Comments

Aug 27, 2013 11:00 AM

NIce!

Marcus Granström
Marcus Granström Aug 27, 2013 11:10 AM

Love the player names! Great blog post

Aug 27, 2013 03:14 PM

Brilliant! A little bit disappointed with Michu's performance this month though.

Per Magne Skuseth
Per Magne Skuseth Aug 28, 2013 09:59 AM

Thanks! I'm sure Michu will be better next month. Saving a spot for him on my fpl team.

Oct 4, 2013 02:42 PM

Nice work, thanks for sharing. The IndexOf method on the Contents property of ContentArea, did you write that yourself? Can't find it as part of LINQ, nor as an extension method in the EPiServer namespaces.

Per Magne Skuseth
Per Magne Skuseth Oct 4, 2013 03:01 PM

Yes, that is an extension I made:

public static int IndexOf(this IEnumerable source, T value)
{
int index = 0;
var comparer = EqualityComparer.Default;
foreach (T item in source)
{
if (comparer.Equals(item, value)) return index;
index++;
}
return -1;
}

Sigve Fast
Sigve Fast Dec 12, 2013 04:15 PM

How do you get the current ContentArea tag when using a page as a block?ControllerContext.ParentActionViewContext.ViewData["Tag"] as string; does not work.

Per Magne Skuseth
Per Magne Skuseth Dec 12, 2013 04:29 PM

Sigve, it works for me. Did a simple test:
[TemplateDescriptor(
TemplateTypeCategory = TemplateTypeCategories.MvcPartialController,
Inherited = true)]
public class TestPartialController : PageController

{
public ActionResult Index(SitePageData currentPage)

{
var tag = ControllerContext.ParentActionViewContext.ViewData["Tag"] as string;
return View(currentPage);
}
}

Feb 14, 2014 03:00 PM

What if you had the same block added twice to the same ContentArea?

Per Magne Skuseth
Per Magne Skuseth Feb 17, 2014 08:55 AM

I guess you would need to some extra logic in order to get that working, Alf. Like saving some sort of index key in the TempData. However, with EPiServer 7.5, using DisplayOptions may be a better option than this approach.

Nov 5, 2014 12:58 PM

Is it possible to get the actual ContentArea using webforms too?

Per Magne Skuseth
Per Magne Skuseth Nov 5, 2014 01:03 PM

Haven't tried it, but I suppose you could. You should be able to get the current page with:
var pageRouteHelper = EPiServer.ServiceLocation.ServiceLocator.Current.GetInstance();
PageReference currentPageLink = pageRouteHelper.PageLink;

Andrew Sanin
Andrew Sanin Nov 6, 2014 10:04 AM

Hi Per
Can't figure out how to get the Name of ContentArea. I would like to render different html in the block depending on which area it is on the page. Would appreciate if you could share your ideas.

Andrew Sanin
Andrew Sanin Nov 6, 2014 10:50 AM

A solution to my problem, that I could find is to pass additional info (area name) into ViewData when rendering the area:

@Html.PropertyFor(m => m.SideArea, new { ContentAreaName="SideArea"})

Then it's accessible in the block controller as you described.

Per Magne Skuseth
Per Magne Skuseth Nov 6, 2014 01:59 PM

Andrew,
it sounds to me like you should rather use tags and template descriptors in your situations. If you are not familiar with template descriptors your should check out the TeaserBlockWide in the Alloy templates.

Nino Mengarelli
Nino Mengarelli Mar 6, 2015 04:06 PM

Hi! Thanks for a nice post!
I found that the ContentArea.Contents property is now deprecated so i did this instead using the Items.

index = currentContentArea.Items.Select(p=>p.ContentLink).IndexOf(CurrentData.ContentLink) + 1;

And that did the job for me!

Please login to comment.
Latest blogs
Optimizely community meetup - Sept 29 (virtual + Melbourne)

Super excited to be presenting this Thursday the 29th of September at the Optimizely community meetup. For the full details and RSVP's see the...

Ynze | Sep 27, 2022 | Syndicated blog

Preview multiple Visitor Groups directly while browsing your Optimizely site

Visitor groups are great - it's an easy way to add personalization towards market segments to your site. But it does come with it's own set of...

Allan Thraen | Sep 26, 2022 | Syndicated blog

The Report Center is finally back in Optimizely CMS 12

With Episerver.CMS.UI 12.12.0 the Report Center is finally re-introduced in the core product.

Tomas Hensrud Gulla | Sep 26, 2022 | Syndicated blog

Dynamic Route in ASP.NET Core When MapDynamicControllerRoute Does Not Work

Background Creating one of the add-on for Optimizely I had to deal with challenge to register dynamically route for the API controller. Dynamic rou...

valdis | Sep 25, 2022 | Syndicated blog