Try our conversational search powered by Generative AI!

Anders Hattestad
Aug 25, 2014
  8101
(4 votes)

Extending ContentArea to use custom CSS class on child elements

In EPiServer 7.5 there is support for selection DisplayOption for items in a ContentArea. The build in support will change the tag the item is rendered with. But if you want to render it with the default tag and just add some CSS to the item this is a way of doing it.
image
First, I made myself a DisplayOptionWithCss class
public class DisplayOptionWithCss : DisplayOption
{
    public DisplayOptionWithCss() { }
    public DisplayOptionWithCss(string id, string name,string tag,string iconClass,string cssClass)
    {
        this.Id = id;
        this.Name = name;
        this.Tag = tag;
        this.IconClass = iconClass;
        this.CssClass = cssClass;
    }
    public string CssClass { get; set; }
}
Second I added the options inside the Global.asax
public class EPiServerApplication : EPiServer.Global
{
protected void Application_Start()
{
    var options = ServiceLocator.Current.GetInstance<DisplayOptions>();
    options
        .Add(new DisplayOptionWithCss(ContentAreaTags.Col12, "12/12 width", "", "col12 LayoutGrid", "col-sm-12"))
        .Add(new DisplayOptionWithCss(ContentAreaTags.Col8, "8/12 width", "", "col8 LayoutGrid", "col-md-8 col-sm-6 col-xs-12"))
        .Add(new DisplayOptionWithCss(ContentAreaTags.Col6, "6/12 width", "", "col6 LayoutGrid", "col-md-6 col-sm-6 col-xs-12"))
        .Add(new DisplayOptionWithCss(ContentAreaTags.Col4, "4/12 width", "", "col4 LayoutGrid", "col-md-4 col-sm-6 col-xs-12"))
        .Add(new DisplayOptionWithCss(ContentAreaTags.Col3, "3/12 width", "", "col3 LayoutGrid", "col-md-3 col-sm-4 col-xs-6"))
        .Add(new DisplayOptionWithCss(ContentAreaTags.Col2, "2/12 width", "", "col2 LayoutGrid", "col-md-2 col-sm-4 col-xs-12"));
    AreaRegistration.RegisterAllAreas();
}
public static class ContentAreaTags
{
    public const string Col2 = "Col2";
    public const string Col3 = "Col3";
    public const string Col4 = "Col4";
    public const string Col6 = "Col6";
    public const string Col8 = "Col8";
    public const string Col12 = "Col12";
}
Third, I added my custom css to /module.config
<?xml version="1.0" encoding="utf-8"?>
<module>
  <assemblies>
  </assemblies>
  <clientResources>
    <add name="epi-cms.widgets.base" path="Styles/Styles.css" resourceType="Style"/>
  </clientResources>
  <dojo>
    <!-- Add a mapping from alloy to ~/ClientResources/Scripts to the dojo loader configuration -->
    <paths>
      <add name="alloy" path="Scripts" />
    </paths>
  </dojo>
</module>
and added my Css inside Styles.css. This code don’t use any images.
.Sleek .LayoutGrid {
  height: 28px;
  width: 44px;
  
}
.Sleek .LayoutGrid {
  height: 28px;
  width: 44px;
}
.Sleek .LayoutGrid:before {
 content:'';
  display:block;
  float:left;
  background-color:silver;
  border:1px solid silver;
  margin:1px;
  height: 24px;
  padding:0;
}
.Sleek .LayoutGrid:after {
  content:'';
  display:block;
  float:left;
  background-color:white;
  margin:1px;
  padding:0;
  border:1px solid silver;
  height: 24px;
}
.Sleek .LayoutGrid.col12:before {
  width: 40px;
}
.Sleek .LayoutGrid.col12:after {
    display:none;
  width: 12px;
}
.Sleek .LayoutGrid.col8:before {
  width: 24px;
}
.Sleek .LayoutGrid.col8:after {
  width: 12px;
}

.Sleek .LayoutGrid.col6:before {
  width: 18px;
}
.Sleek .LayoutGrid.col6:after {
  width: 18px;
}

.Sleek .LayoutGrid.col4:before {
  width: 12px;
}
.Sleek .LayoutGrid.col4:after {
  width: 24px;
}

.Sleek .LayoutGrid.col3:before {
  width: 9px;
}
.Sleek .LayoutGrid.col3:after {
  width: 27px;
}
.Sleek .LayoutGrid.col2:before {
  width: 6px;
}
.Sleek .LayoutGrid.col2:after {
  width: 30px;
}
Forth, I created my own content render ContentAreaRendererWithDisplayOptionWithCss
Added a default Css render value DefaultChildrenCssClass that will be used if no Css Class is defined
@Html.PropertyFor(p => p.CurrentPage.Common_Tasks, new { CssClass = "row", ChildrenCustomTagName = "div", DefaultChildrenCssClass = "col-md-3",Tag="Box" })
public class ContentAreaRendererWithDisplayOptionWithCss : ContentAreaRenderer
{
    private readonly DisplayOptions _displayOptionsOwn;
    private readonly IContentRepository _contentRepositoryOwn;
    public ContentAreaRendererWithDisplayOptionWithCss()
        : this(ServiceLocator.Current.GetInstance<IContentRenderer>(), ServiceLocator.Current.GetInstance<TemplateResolver>(), ServiceLocator.Current.GetInstance<ContentFragmentAttributeAssembler>(), ServiceLocator.Current.GetInstance<IContentRepository>(), ServiceLocator.Current.GetInstance<DisplayOptions>())
    {
    }
    public ContentAreaRendererWithDisplayOptionWithCss(IContentRenderer contentRenderer, TemplateResolver templateResolver, ContentFragmentAttributeAssembler attributeAssembler, IContentRepository contentRepository, DisplayOptions displayOptions)
        : base(contentRenderer, templateResolver, attributeAssembler, contentRepository, displayOptions)
    {
        _displayOptionsOwn = displayOptions;
        _contentRepositoryOwn = contentRepository;
    }

    public string DefaultChildrenCssClass { get; set; }
    protected override void RenderContentAreaItem(HtmlHelper htmlHelper, ContentAreaItem contentAreaItem, string templateTag, string htmlTag, string cssClass)
    {
        ViewContext viewContext = htmlHelper.ViewContext;
        DefaultChildrenCssClass = viewContext.ViewData["defaultchildrencssclass"] as string;
        IContent content = contentAreaItem.GetContent(this._contentRepositoryOwn);
        if (content == null)
        {
            return;
        }
        var templateModel = this.ResolveTemplate(htmlHelper, content, templateTag);
            
        base.RenderContentAreaItem(htmlHelper, contentAreaItem, templateTag, htmlTag,"tag_"+templateTag+" resolver_"+((templateModel==null)?"none":templateModel.Name)+" "+ cssClass);
    }

    protected override string GetContentAreaItemTemplateTag(HtmlHelper htmlHelper, ContentAreaItem contentAreaItem)
    {
        DisplayOption displayOption = LoadDisplayOption(contentAreaItem);
        if (displayOption != null && !string.IsNullOrEmpty(displayOption.Tag))
        {
            return displayOption.Tag;
        }
        return this.GetContentAreaTemplateTag(htmlHelper);
    }

    protected override void BeforeRenderContentAreaItemStartTag(System.Web.Mvc.TagBuilder tagBuilder, EPiServer.Core.ContentAreaItem contentAreaItem)
    {
        var displayOption = LoadDisplayOption(contentAreaItem) as DisplayOptionWithCss;

        if (displayOption != null && !string.IsNullOrEmpty(displayOption.CssClass))
        {
            tagBuilder.AddCssClass(displayOption.CssClass);
            if (displayOption != null)
                tagBuilder.Attributes["data-displayOption"] = displayOption.Id;
        }
        else
            AddNonEmptyCssClass(tagBuilder, DefaultChildrenCssClass);
    }
      

    protected DisplayOption LoadDisplayOption(ContentAreaItem contentAreaItem)
    {
        string displayOptionId = null;
        if (contentAreaItem.RenderSettings != null && contentAreaItem.RenderSettings.ContainsKey(EPiServer.Core.Html.StringParsing.ContentFragment.ContentDisplayOptionAttributeName))
            displayOptionId = "" + contentAreaItem.RenderSettings[EPiServer.Core.Html.StringParsing.ContentFragment.ContentDisplayOptionAttributeName];
        if (string.IsNullOrEmpty(displayOptionId))
            return null;
        return _displayOptionsOwn.Get(displayOptionId);
    }
}
Finally, I changed the default render to use my own contentarea render
[InitializableModule]
public class DependencyResolverInitialization : IConfigurableModule
{
    public void ConfigureContainer(ServiceConfigurationContext context)
    {
        context.Container.Configure(ConfigureContainer);
    }

    private static void ConfigureContainer(ConfigurationExpression container)
    {
        //Swap out the default ContentRenderer for our custom
        // container.For<IContentRenderer>().Use<ErrorHandlingContentRenderer>();
        container.For<ContentAreaRenderer>().Use<ContentAreaRendererWithDisplayOptionWithCss>();

        //Implementations for custom interfaces can be registered here.
    }

    public void Initialize(InitializationEngine context)
    {
    }

    public void Uninitialize(InitializationEngine context)
    {
    }

    public void Preload(string[] parameters)
    {
    }
}
And that’s it. Now you can have the display option to change the CSS of the items in a contentarea and have it to change the tag.
Aug 25, 2014

Comments

Frederik Vig
Frederik Vig Aug 25, 2014 08:48 PM

Couldn´t you just use tags, read the tags in the view and apply different css class based on that tag, or am I missing something? :)

Frederik

Anders Hattestad
Anders Hattestad Aug 25, 2014 08:52 PM

Yes you could do that, but then you need to add the css to every view file. I don't know about you, but I often forget some views when I need to do it on all :)

valdis
valdis Aug 26, 2014 04:33 PM

Nice, I had similar solution just with Bootstrap awareness :)
http://tech-fellow.net/2014/02/11/bootstrap-aware-episerver-content-area-render/

Apr 24, 2017 04:50 PM

Please login to comment.
Latest blogs
Introducing Image Transformer - AI Assistant for Optimizely

We've got something super cool to share with you, and it's all about giving your images a fresh spin. Image Transformer, the latest feature from ou...

Luc Gosso (MVP) | Feb 26, 2024 | Syndicated blog

Welcome 2024 Winter OMVPs

Hello, Optimizely community! We are thrilled to announce and welcome the newest members to the Optimizely Most Valuable Professionals (OMVP) progra...

Patrick Lam | Feb 26, 2024

Optimizely Opal... what it does actually do?

At Opticon 2023, Optimizely announced its first AI product Opal. AI is definitely the new tech buzzword in 2024 and with promises that AI will be...

Jon Jones | Feb 25, 2024 | Syndicated blog

How to add more Content Area Context Menu Item in Optimizely CMS 12

Hey folks, today I will share something related to Context Menu customization in the Content Area of Optimizely CMS. As you know, the content area ...

Binh Nguyen | Feb 25, 2024

Developer meetups in Stockholm & Helsinki

It's time for developer meetups! Next month we will be in Stockholm and Helsinki. Join us for getting the latest updates from Optimizely, be inspir...

Magnus Kjellander | Feb 23, 2024

Roll Your Own Security Headers

Proper security headers are a must for your Optimizely driven website. There are a variety of tools out there that will help with this, but when...

Ethan Schofer | Feb 21, 2024