Home » FieldRenderer

Category Archives: FieldRenderer

Omit HTML Breaks From Rendered Multi-Line Text Fields in Sitecore

Earlier today while preparing a training session on how to add JavaScript from a Multi-Line Text field to a rendered Sitecore page, I encountered something I had seen in the past but forgot about: Sitecore FieldControls and the FieldRenderer Web Control will convert newlines into HTML breaks.

For example, suppose you have the following JavaScript in a Multi-Line Text field:

javascript-in-field

You could use a Text FieldControl to render it:

<%@ Control Language="c#" AutoEventWireup="true" TargetSchema="http://schemas.microsoft.com/intellisense/ie5" %>
<sc:Text ID="scJavaScript" Field="JavaScript" runat="server" />

Unfortunately, your JavaScript will not work since it will contain HTML breaks:

html-breaks-in-rendered-value

Why does this happen? In the RenderField() method in Sitecore.Web.UI.WebControls.FieldRenderer — this lives in Sitecore.Kernel.dll, and is called by all FieldControls — passes a “linebreaks” parameter to the <renderField> pipeline:

field-renderer-html-breaks

The Process() method in Sitecore.Pipelines.RenderField.GetMemoFieldValue — this serves as one of the “out of the box” processors of the <renderField> pipeline — converts all carriage returns, line feeds, and newlines into HTML breaks:

get-memo-field-value

What can we do to prevent this from happening? Well, you could spin up a new class with a Process() method to serve as a new <renderField> pipeline processor, and use that instead of Sitecore.Pipelines.RenderField.GetMemoFieldValue:

using System;

using Sitecore.Diagnostics;
using Sitecore.Pipelines.RenderField;

namespace Sitecore.Sandbox.Pipelines.RenderField
{
    public class GetRawMemoFieldValueWhenApplicable
    {
        public void Process(RenderFieldArgs args)
        {
            Assert.ArgumentNotNull(args, "args");
            if(!AreEqualIgnoreCase(args.FieldTypeKey, "memo") && !AreEqualIgnoreCase(args.FieldTypeKey, "multi-line text"))
            {
                return;
            }

            bool omitHtmlBreaks;
            if (bool.TryParse(args.Parameters["omitHtmlBreaks"], out omitHtmlBreaks))
            {
                return;
            }

            Assert.IsNotNull(DefaultGetMemoFieldValueProcessor, "DefaultGetMemoFieldValueProcessor must be set in your configuration!");
            DefaultGetMemoFieldValueProcessor.Process(args);
        }

        private static bool AreEqualIgnoreCase(string stringOne, string stringTwo)
        {
            return string.Equals(stringOne, stringTwo, StringComparison.CurrentCultureIgnoreCase);
        }

        private GetMemoFieldValue DefaultGetMemoFieldValueProcessor { get; set; }
    }
}

The Process() method in the class above looks for an “omitHtmlBreaks” parameter, and just exits out of the Process() method when it is set to true — it leaves the field value “as is”.

If the “omitHtmlBreaks”parameter is not found in the RenderFieldArgs instance, or it is set to false, the Process() method delegates to the Process() method of its DefaultGetMemoFieldValueProcessor property — this would be an instance of the “out of the box” Sitecore.Pipelines.RenderField.GetMemoFieldValue, and this is passed to the new <renderField> pipeline processor via the following configuration file:

<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <pipelines>
      <renderField>
        <processor patch:instead="processor[@type='Sitecore.Pipelines.RenderField.GetMemoFieldValue, Sitecore.Kernel']"
                   type="Sitecore.Sandbox.Pipelines.RenderField.GetRawMemoFieldValueWhenApplicable, Sitecore.Sandbox">
          <DefaultGetMemoFieldValueProcessor type="Sitecore.Pipelines.RenderField.GetMemoFieldValue, Sitecore.Kernel" />
        </processor>  
      </renderField>
    </pipelines>
  </sitecore>
</configuration>

Let’s test this.

I added the “omitHtmlBreaks” parameter to the control I had shown above:

<%@ Control Language="c#" AutoEventWireup="true" TargetSchema="http://schemas.microsoft.com/intellisense/ie5" %>
<sc:Text ID="scJavaScript" Field="JavaScript" Parameters="omitHtmlBreaks=true" runat="server" />

When I loaded my page, I was given a warm welcome:

alert-box-welcome

When I viewed my page’s source, I no longer see HTML breaks:

javascript-no-html-breaks

If you have any thoughts on this, or know of another way to do this, please share in a comment.

Yes — You, Too, Can Build a Custom Field Control!

Ever since I started building websites in Sitecore over five years ago, I had an itch to build a custom Sitecore Field Control. Sadly, I never put my nose to the grindstone in doing so until a few weeks ago. Armed with .NET Reflector and the Sitecore.Kernel.dll, I sat down and finally built one. This post highlights the fruits of my endeavor.

First, let me give you a quick description on what a Sitecore Field Control is. A Sitecore Field Control is an ASP.NET Web Control that presents data set in a Sitecore field according to logic defined by the control. Sitecore offers a few Field Controls out of the box — these include Image, Link, Text, Date — and all are defined within the namespace Sitecore.Web.UI.WebControls. How to use these Field Controls is illustrated during Sitecore Developer Training and certification.

In this example, I created a custom Field Control that takes a value set within a Single-Line Text, Multi-Line Text, Rich Text, or text field — a deprecated field type used before Sitecore v5.3.1. — and wraps the value around HTML specified by client code. This HTML tag is defined by the HtmlTag attribute of the HtmlTagFieldControl below.

First, I created a new Field Control:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.UI;

using Sitecore.Collections;
using Sitecore.Web.UI.WebControls;

namespace Sitecore.Sandbox.FieldControls
{
    public class HtmlTagFieldControl : FieldControl
    {
        public string HtmlTag { get; set; }

        protected override void PopulateParameters(SafeDictionary<string> parameters)
        {
            base.PopulateParameters(parameters);

            if (!string.IsNullOrEmpty(HtmlTag))
            {
                parameters.Add("htmlTagName", HtmlTag);
            }
            
            foreach (string attributeName in base.Attributes.Keys)
            {
                string attributeValue = base.Attributes[attributeName];
                parameters.Add(attributeName, attributeValue);
            }
        }
    }
}

Next, I built a renderer — including a Data transfer object for passing arguments to the renderer — for the field control defined above:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Sitecore.Xml.Xsl;

namespace Sitecore.Sandbox.Renderers.Base
{
    public interface IHtmlTagRenderer
    {
        RenderFieldResult Render();
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;

using Sitecore;
using Sitecore.Collections;
using Sitecore.Data.Fields;
using Sitecore.Data.Items;
using Sitecore.Diagnostics;
using Sitecore.Xml.Xsl;

using Sitecore.Sandbox.Renderers.Base;
using Sitecore.Sandbox.Renderers.DTO;

namespace Sitecore.Sandbox.Renderers
{
    public class HtmlTagRenderer : FieldRendererBase, IHtmlTagRenderer
    {
        private const string OpenHtmlTagFormat = "<{0}>";
        private const string CloseHtmlTagFormat = "</{0}>";

        private HtmlTagRendererArgs Arguments { get; set; }

        private HtmlTagRenderer(HtmlTagRendererArgs arguments)
            : base()
        {
            SetArguments(arguments);
        }

        private void SetArguments(HtmlTagRendererArgs arguments)
        {
            Assert.ArgumentNotNull(arguments, "arguments");
            Arguments = arguments;
        }

        public virtual RenderFieldResult Render()
        {
            string fieldHtml = CreateFieldHtml();
            return new RenderFieldResult(fieldHtml);
        }

        private string CreateFieldHtml()
        {
            string openHtmlTag = GetOpenHtmlTag();
            string closeHtmlTag = GetCloseHtmlTag();

            return string.Concat(openHtmlTag, Arguments.FieldValue, closeHtmlTag);
        }

        private string GetOpenHtmlTag()
        {
            return GetTagHtml(OpenHtmlTagFormat, Arguments.HtmlTagName);
        }

        private string GetCloseHtmlTag()
        {
            return GetTagHtml(CloseHtmlTagFormat, Arguments.HtmlTagName);
        }

        private static string GetTagHtml(string tagFormat, string tagName)
        {
            if (!string.IsNullOrEmpty(tagName))
            {
                return string.Format(tagFormat, tagName);
            }

            return string.Empty;
        }

        public static IHtmlTagRenderer Create(HtmlTagRendererArgs arguments)
        {
            return new HtmlTagRenderer(arguments);
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Sitecore.Sandbox.Renderers.DTO
{
    public class HtmlTagRendererArgs
    {
        public string HtmlTagName { get; set; }
        public string FieldName { get; set; }
        public string FieldValue { get; set; }
    }
}

Last, but not least, I defined a Render Field pipeline to glue all the pieces together — including the pivotal piece of wrapping the field’s value around the wrapper HTML tag:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Sitecore.Data.Items;
using Sitecore.Diagnostics;
using Sitecore.Pipelines.RenderField;
using Sitecore.Xml.Xsl;

using Sitecore.Sandbox.Renderers;
using Sitecore.Sandbox.Renderers.Base;
using Sitecore.Sandbox.Renderers.DTO;

namespace Sitecore.Sandbox.Pipelines.RenderField
{
    public class GetHtmlTagFieldControlHtml
    {
        private static IEnumerable<string> supportedFieldTypes = new string[] { "text", "rich text", "single-line text", "multi-line text" };

        protected virtual IHtmlTagRenderer CreateRenderer(HtmlTagRendererArgs htmlTagRendererArgs)
        {
            return HtmlTagRenderer.Create(htmlTagRendererArgs);
        }

        public void Process(RenderFieldArgs renderFieldArgs)
        {
            if (CanFieldBeProcessed(renderFieldArgs))
            {
                SetRenderFieldArgsResults(renderFieldArgs);
            }
        }

        private bool CanFieldBeProcessed(RenderFieldArgs renderFieldArgs)
        {
            string fieldTypeKey = renderFieldArgs.FieldTypeKey.ToLower();
            return supportedFieldTypes.Contains(fieldTypeKey);
        }

        private void SetRenderFieldArgsResults(RenderFieldArgs renderFieldArgs)
        {
            RenderFieldResult renderFieldResult = GetRenderFieldResult(renderFieldArgs);
            SetRenderFieldArgsResultHtmlProperties(renderFieldResult, renderFieldArgs);
        }

        private RenderFieldResult GetRenderFieldResult(RenderFieldArgs renderFieldArgs)
        {
            HtmlTagRendererArgs htmlTagRendererArgs = CreateHtmlTagRendererArgs(renderFieldArgs);
            IHtmlTagRenderer htmlTagRenderer = CreateRenderer(htmlTagRendererArgs);
            return htmlTagRenderer.Render();
        }

        private HtmlTagRendererArgs CreateHtmlTagRendererArgs(RenderFieldArgs renderFieldArgs)
        {
            string htmlTagName = string.Empty;

            if (renderFieldArgs.Parameters.ContainsKey("htmlTagName"))
            {
                htmlTagName = renderFieldArgs.Parameters["htmlTagName"];
            }

            return new HtmlTagRendererArgs
            {
                HtmlTagName = htmlTagName,
                FieldName = renderFieldArgs.FieldName,
                FieldValue = renderFieldArgs.FieldValue
            };
        }

        private void SetRenderFieldArgsResultHtmlProperties(RenderFieldResult renderFieldResult, RenderFieldArgs renderFieldArgs)
        {
            renderFieldArgs.Result.FirstPart = renderFieldResult.FirstPart;
            renderFieldArgs.Result.LastPart = renderFieldResult.LastPart;
        }
    }
}

I added four fields to a data template — each being of a type supported by my custom Field Control:

I added some content:

I wrote some code for the test sublayout used in this example — notice how I’m having a little fun by using the marquee tag :):

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="My Little Sublayout.ascx.cs" Inherits="Sitecore650rev120706.layouts.sublayouts.MyLittleSublayout" %>
<div>
	<strong>Field Name:</strong>&nbsp;title<br />
	<strong>Field Type:</strong>&nbsp;text<br />
	<strong>Html Tag:</strong>&nbsp;h1<br />
	<strong>Field Control Html:</strong><br />
    <sj:HtmlTagFieldControl ID="h1Title" HtmlTag="h1" Field="Title" runat="server" />
    <hr />
</div>
<div>
	<strong>Field Name:</strong>&nbsp;Subtitle<br />
	<strong>Field Type:</strong>&nbsp;Single-Line Text<br />
	<strong>Html Tag:</strong>&nbsp;h2<br />
	<strong>Field Control Html:</strong><br />
    <sj:HtmlTagFieldControl ID="h2Subtitle" HtmlTag="h2" Field="Subtitle" runat="server" />
    <hr />
</div>
<div>
	<strong>Field Name:</strong>&nbsp;Blurb<br />
	<strong>Field Type:</strong>&nbsp;Multi-Line Text<br />
	<strong>Html Tag:</strong>&nbsp;blockquote<br />
	<strong>Field Control Html:</strong>
    <sj:HtmlTagFieldControl ID="HtmlTagFieldControl1" HtmlTag="blockquote" Field="Blurb" runat="server" />
    <hr />
</div>
<div>
	<strong>Field Name:</strong>&nbsp;Text<br />
	<strong>Field Type:</strong>&nbsp;Rich Text<br />
	<strong>Html Tag:</strong>&nbsp;marquee<br />
	<strong>Field Control Html:</strong>
    <sj:HtmlTagFieldControl ID="marqueeText" HtmlTag="marquee" Field="Text" runat="server" />
</div>

Rendered HTML:

Sitecore offering the ability to create custom Field Controls is just another example of its extensibility. I don’t know of many developers that have taken advantage of creating their own custom Field Controls. However, I hope this post kindles the want for doing so, or adds some armament to your arsenal on how to present Sitecore data.

Addendum

Recently, the following query was asked on twitter:

Custom field controls – would you write your own from scratch, or inherit e.g. sc:text and add what you need (and why)?

The Text FieldControl doesn’t offer any logic over it’s FieldControl base — it inherits directly from the FieldControl class without any overrides. Inheriting from it will not add any extra benefit.

Further, the only method that might be of interest in overriding in a FieldControl base class — a class other than Text — would be the PopulateParameters method — albeit an argument could be made for overriding the DoRender method, but I will not discuss this method here. The PopulateParameters method sets parameters that are used by the RendererField pipeline for passing arguments to the Renderer object — the latter object creates the HTML for the control.

Plus, as a general best practice, I would caution against inheriting from classes unless absolutely necessary — more specific FieldControl classes within Sitecore.Web.UI.WebControls are no exceptions. You don’t want to introduce a steep class hierarchy, or cause class explosion — a situation where you create a new class just to add new functionality over a base class (Vlissides, Helm, Johnson, Gamma, 1995; Shalloway and Trott, 2004). Utilizing composition is recommended over inheritance — a model that faciliates in code maintenance, refactoring efforts, and adding new functionality (Vlissides, Helm, Johnson, & Gamma, 1995; Shalloway & Trott, 2004).

The real magic though occurs within the Renderer and RenderField pipeline classes, not the FieldControl classes, per se.

References

Gamma, E., Helm, R., Johnson, R., & Vlissides, J. (1995). Design patterns: Elements of reusable object-oriented software. Reading: Addison-Wesley.

Shalloway, A., Trott, J. A. (2004) Design Patterns Explained. A new Perspective on Object-Oriented Design (2nd edition). Reading: Addison-Wesley.

Labels and Literals and Field Controls, Oh My!

I wish I could tell you I’ve discovered the panacea to extirpate common issues inherent in many Sitecore development projects and this article would enumerate a recipe for curtailing these, or how I’ve discovered a hidden gem within Sitecore.Kernel.dll that your future projects cannot live without.

Sadly, this article will do none of these things — it will not knock your socks off, and may even leave you saying to yourself “come on Mike, tell me something I don’t already know”.

Yet, I feel adamantly compelled to pen this article given an egregious practice I’ve seen employed by numerous Sitecore developers at multiple Sitecore development shops over the span of five years.

What is this foul practice?  It’s the act of building HTML in your C# code using content from Sitecore fields and displaying this HTML via ASP.NET Literal or Label Web Controls.

No doubt, you have seen some code like the following in a Sitecore solution, or have even written code like this yourself (please note these code examples haven’t been tested, aren’t robust, and are only given as pedagogical illustrations):

An ASP.NET User Control containing a Literal Web Control:

Code-behind for the User Control:

In the above code-behind, the author composed some link HTML using content within a Sitecore General Link field named ‘My Link’.

Compare that with the following:

An ASP.NET User Control containing Sitecore Link Field Control:

Code-behind for the User Control:

Notice how much less code was needed to wire-up the Link Field Control.  The Link Field Control will render link HTML if the current Item has a link set on the ‘My Link’ General Link field — including the target attribute, if a target is set on the General Link field.

You might be saying “Mike, this is great, but we don’t want this working in the Page Editor — we want this field to be completely locked down since it’s in a global Item”.  Well, you could use the following to disable your Field Control in the Page Editor, and have the ability to change it later without having to do a full code build (assuming you’re using the Web Application model in ASP.NET):

If you are uncertain about which Field Control to use, you could always poke around in Sitecore.Web.UI.WebControls using .NET Reflector, or could opt to use a FieldRenderer Web Control — all Sitecore Field Controls use an instance of a FieldRenderer to output their HTML:

Using a FieldRenderer would also aid in allowing you to change a field’s type later without breaking its presentation in the UI — as long as the field type can be rendered by the FieldRenderer.

That’s it for now.  Happy coding!

-Mike