Home » Web Control

Category Archives: Web Control

Advertisements

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.

Advertisements

Add the HTML5 Range Input Control into Web Forms for Marketers in Sitecore

A couple of weeks ago, I was researching what new input controls exist in HTML5 — I am quite a dinosaur when it comes to front-end code, and felt it was a good idea to see what is currently available or possible — and discovered the range HTML control:

range-example

I immediately wanted to add this HTML5 input control into Web Forms for Marketers in Sitecore, and built the following control class as a proof of concept:

using System;
using System.Web.UI;
using System.Web.UI.WebControls;

using Sitecore.Diagnostics;
using Sitecore.Form.Web.UI.Controls;

namespace Sitecore.Sandbox.Form.Web.UI.Controls
{
    public class Range : InputControl
    {
        public Range() : this(HtmlTextWriterTag.Div)
        {
        }

        public Range(HtmlTextWriterTag tag)
            : base(tag)
        {
        }

        protected override void OnInit(EventArgs e)
        {
            base.textbox.CssClass = "scfSingleLineTextBox";
            base.help.CssClass = "scfSingleLineTextUsefulInfo";
            base.generalPanel.CssClass = "scfSingleLineGeneralPanel";
            base.title.CssClass = "scfSingleLineTextLabel";
            this.Controls.AddAt(0, base.generalPanel);
            this.Controls.AddAt(0, base.title);
            base.generalPanel.Controls.AddAt(0, base.help);
            base.generalPanel.Controls.AddAt(0, textbox);
        }

        protected override void DoRender(HtmlTextWriter writer)
        {
            textbox.Attributes["type"] = "range";
            TrySetIntegerAttribute("min", MinimumValue);
            TrySetIntegerAttribute("max", MaximumValue);
            TrySetIntegerAttribute("step", StepInterval);
            EnsureDefaultValue();
            textbox.MaxLength = 0;
            base.DoRender(writer);
        }

        protected virtual void TrySetIntegerAttribute(string attributeName, string value)
        {
            int integerValue;
            if (int.TryParse(value, out integerValue))
            {
                SetAttribute(attributeName, integerValue.ToString());
            }
        }

        protected virtual void SetAttribute(string attributeName, string value)
        {
            Assert.ArgumentNotNull(textbox, "textbox");
            Assert.ArgumentNotNullOrEmpty(attributeName, "attributeName");
            textbox.Attributes[attributeName] = value;
        }

        protected virtual void EnsureDefaultValue()
        {
            int value;
            if (!int.TryParse(Text, out value))
            {
                textbox.Text = string.Empty;
            }
        }

        public string MinimumValue { get; set; }

        public string MaximumValue { get; set; }

        public string StepInterval { get; set; }
    }
}

Most of the magic behind how this works occurs in the DoRender() method above. In that method we are changing the “type” attribute on the TextBox instance defined in the parent InputControl class to be “range” instead of “text”: this is how the browser will know that it is to render a range control instead of a textbox.

The DoRender() method also delegates to other helper methods: one to set the default value for the control, and another to add additional attributes to our control — the “step”, “min”, and “max” attributes in particular (you can learn more about these attributes by reading this specification for the range control) — and these are only set when values are passed to our code via XML defined in Sitecore for the control:

range-field-type

Let’s test this out!

I whipped up a test form, and added a range field to it:

range-form-test

This is what the form looked like on the page before I clicked the submit button (trust me, that’s 75 😉 ):

range-form-before-submit

After clicking submit, I was given a confirmation message:

range-form-after-submit

As you can see in the Form Reports for our test form, the value selected on the range control was captured:

range-form-reports

I will admit that I had a lot of fun adding this range input control into Web Forms for Marketers but do question whether anyone would use this control.

Why?

I found no way to add label markers for the different values on the control (if you are aware of a way to do this, please leave a comment).

Also, it should be noted that this control will not work in Internet Explorer 9 or earlier versions.

If you can think of ways around making this better, or have ideas for other HTML5 controls that could/should be added to Web Forms for Marketers, please share in a comment.

Specify Which Sitecore Web Forms for Marketers Form To Render Via the Query String

Today I saw a post in one of the SDN forums asking how one could go about building a page in Sitecore that can render a Web Forms for Marketers (WFFM) form based on an ID passed via the query string.

I built the following WFFM FormRenderer as a “proof of concept” to accomplish this — this solution assumes the ID we are passing is the ID of the form, and not some other ID (or guid):

using System;
using System.Web;

using Sitecore.Data.Items;
using Sitecore.Diagnostics;
using Sitecore.Form.Core.Configuration;
using Sitecore.Form.Core.Renderings;

namespace Sitecore.Sandbox.Form.Core.Renderings
{
    public class DetectIDFormRenderer : FormRender
    {
        protected override void OnInit(System.EventArgs e)
        {
            string detectedFormId = GetDetectedFormId();
            if (IsValidFormId(detectedFormId))
            {
                FormID = detectedFormId;
            }
            
            base.OnInit(e);
        }

        private static string GetDetectedFormId()
        {
            return HttpContext.Current.Request["formId"];
        }

        private static bool IsValidFormId(string id)
        {
            return !string.IsNullOrWhiteSpace(id) 
                    && IsID(id) 
                    && IsFormId(id);
        }
        
        private static bool IsID(string id)
        {
            Sitecore.Data.ID sitecoreID;
            return Sitecore.Data.ID.TryParse(id, out sitecoreID);
        }

        private static bool IsFormId(string id)
        {
            Item item = StaticSettings.ContextDatabase.GetItem(id);
            return item != null && item.TemplateID == IDs.FormTemplateID;
        }
    }
}

The FormRenderer above grabs the specified form’s ID via a query string parameter, ascertains whether it’s truly an ID, and determines whether it is an ID of a WFFM Form in Sitecore — these are done via the IsID and IsFormId methods.

If the supplied form ID is valid, we save it to the FormID property defined in the base FormerRender class. Otherwise, we flow through to the “out of the box” logic.

Now it’s time to register the above class in Sitecore.

I duplicated the “out of the box” Form Webcontrol under /sitecore/layout/Renderings/Modules/Web Forms for Marketers, renamed the item to something appropriate, and updated the code-defining fields to point to our new FormRender above:

Detect-ID-Form-FormRenderer

I decided to reuse an existing page item with a WFFM form — I didn’t want to step through ‘Insert Form’ wizard so that I could save time — and swapped out the “out of the box” Form Webcontrol with the new one we created above:

webcontrol-switch

I ensured we had a default form set just in case of query string manipulation, or in the event the form cannot be found by the given ID:

default-form-is-set

I published everything, and navigated to my form page:

no-form-specified

I then specified the empty guid:

invalid-form-specified

I manipulated the query string again, but this time passing a valid form ID:

valid-form-specified

I then changed the form ID again but with another valid form ID:

another-valid-form-specified

If you have any suggestions around making this better, or ideas for a different solution, please drop a comment.

Content Manage Links to File System Favicons for Multiple Sites Managed in Sitecore

Earlier today someone started a thread in one of the SDN forums asking how to go about adding the ability to have a different favicon for each website managed in the same instance of Sitecore.

I had implemented this in the past for a few clients, and thought I should write a post on how I had done this.

In most of those solutions, the site’s start item would contain a “server file” field — yes I know it’s deprecated but it works well for this (if you can suggested a better field type to use, please leave a comment below) — that would point to a favicon on the file system:

server-file-favicon

Content authors/editors can then choose the appropriate favicon for each site managed in their Sitecore instance — just like this:

linked-to-smiley-favicon

Not long after the SDN thread was started, John West — Chief Technology Officer at Sitecore USA — wrote a quick code snippet, followed by a blog post on how one might go about doing this.

John’s solution is a different than the one I had used in the past — each site’s favicon is defined on its site node in the Web.config.

After seeing John’s solution, I decided I would create a hybrid solution — the favicon set on the start item would have precedence over the one defined on the site node in the Web.config. In other words, the favicon defined on the site node would be a fallback.

For this hybrid solution, I decided to create a custom pipeline to retrieve the favicon for the context site, and created the following pipeline arguments class for it:

using System.Web.UI;

using Sitecore.Pipelines;

namespace Sitecore.Sandbox.Pipelines.GetFavicon
{
    public class FaviconTryGetterArgs : PipelineArgs
    {
        public string FaviconUrl { get; set; }

        public Control FaviconControl{ get; set; }
    }
}

The idea is to have pipeline processors set the URL of the favicon if possible, and have another processor create an ASP.NET control for the favicon when the URL is supplied.

The following class embodies this high-level idea:

using System;

using System.Web.UI;
using System.Web.UI.HtmlControls;

using Sitecore;
using Sitecore.Configuration;
using Sitecore.Data.Fields;
using Sitecore.Data.Items;
using Sitecore.Diagnostics;
using Sitecore.Sandbox.Utilities.Extensions;

namespace Sitecore.Sandbox.Pipelines.GetFavicon
{
    public class FaviconTryGetter
    {
        private string FaviconFieldName { get; set; }

        public void TryGetFromStartItem(FaviconTryGetterArgs args)
        {
            Assert.ArgumentNotNull(args, "args");
            bool canProcess = !string.IsNullOrWhiteSpace(FaviconFieldName) 
                                && Context.Site != null 
                                && !string.IsNullOrWhiteSpace(Context.Site.StartPath);

            if (!canProcess)
            {
                return;
            }

            Item startItem = Context.Database.GetItem(Context.Site.StartPath);
            args.FaviconUrl = startItem[FaviconFieldName];
        }

        public void TryGetFromSite(FaviconTryGetterArgs args)
        {
            Assert.ArgumentNotNull(args, "args");
            bool canProcess = Context.Site != null 
                                && string.IsNullOrWhiteSpace(args.FaviconUrl);

            if (!canProcess)
            {
                return;
            }
            
			/* GetFavicon is an extension method borrowed from John West. You can find it at  http://www.sitecore.net/Community/Technical-Blogs/John-West-Sitecore-Blog/Posts/2013/08/Use-Different-Shortcut-Icons-for-Different-Managed-Sites-with-the-Sitecore-ASPNET-CMS.aspx 
            */
            args.FaviconUrl = Context.Site.GetFavicon(); 
        }

        public void TryGetFaviconControl(FaviconTryGetterArgs args)
        {
            Assert.ArgumentNotNull(args, "args");
            if(string.IsNullOrWhiteSpace(args.FaviconUrl))
            {
                return; 
            }
            
            args.FaviconControl = CreateNewFaviconControl(args.FaviconUrl);
        }

        private static Control CreateNewFaviconControl(string faviconUrl)
        {
            Assert.ArgumentNotNullOrEmpty(faviconUrl, "faviconUrl");
            HtmlLink link = new HtmlLink();
            link.Attributes.Add("type", "image/x-icon");
            link.Attributes.Add("rel", "icon");
            link.Href = faviconUrl;
            return link;
        }
    }
}

The TryGetFromStartItem method tries to get the favicon set on the favicon field on the start item — the name of the field is supplied via one of the processors defined in the configuration include file below — and sets it on the FaviconUrl property of the FaviconTryGetterArgs instance supplied by the caller.

If the field name for the field containing the favicon is not supplied, or there is something wrong with either the context site or the start item’s path, then the method does not finish executing.

The TryGetFromSite method is similar to what John had done in his post. It uses the same exact extension method John had used for getting the favicon off of a “favicon” attribute set on the context site’s node in the Web.config — I have omitted this extension method and its class since you can check it out in John’s post.

If a URL is set by either of the two methods discussed above, the TryGetFaviconControl method creates an instance of an HtmlLink System.Web.UI.HtmlControls.HtmlControl, sets the appropriate attributes for an html favicon link tag, and sets it in the FaviconControl property of the FaviconTryGetterArgs instance.

I assembled the methods above into a new getFavicon pipeline in the following configuration include file, and also set a fallback favicon for my local sandbox site’s configuration element:

<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <pipelines>
      <getFavicon>
        <processor type="Sitecore.Sandbox.Pipelines.GetFavicon.FaviconTryGetter, Sitecore.Sandbox" method="TryGetFromStartItem">
          <FaviconFieldName>Favicon</FaviconFieldName>
        </processor> 
        <processor type="Sitecore.Sandbox.Pipelines.GetFavicon.FaviconTryGetter, Sitecore.Sandbox" method="TryGetFromSite" />
        <processor type="Sitecore.Sandbox.Pipelines.GetFavicon.FaviconTryGetter, Sitecore.Sandbox" method="TryGetFaviconControl" />
      </getFavicon>
    </pipelines>
    <sites>
      <site name="website">
        <patch:attribute name="favicon">/sitecore.ico</patch:attribute>
      </site>
    </sites>
  </sitecore>
</configuration>

Just as John West had done in his post, I created a custom WebControl for rendering the favicon, albeit the following class invokes our new pipeline above to get the favicon ASP.NET control:

using System.Web.UI;

using Sitecore.Pipelines;
using Sitecore.Sandbox.Pipelines.GetFavicon;
using Sitecore.Web.UI;

namespace Sitecore.Sandbox.WebControls
{
    public class Favicon : WebControl 
    {
        protected override void DoRender(HtmlTextWriter output)
        {
            FaviconTryGetterArgs args = new FaviconTryGetterArgs();
            CorePipeline.Run("getFavicon", args);
            if (args.FaviconControl != null)
            {
                args.FaviconControl.RenderControl(output);
            }
        }
    }
}

If a favicon Control is supplied by our new getFavicon pipeline, the WebControl then delegates rendering responsibility to it.

I then defined an instance of the WebControl above in my default layout:

<%@ Register TagPrefix="sj" Namespace="Sitecore.Sharedsource.Web.UI.WebControls" Assembly="Sitecore.Sharedsource" %>
...
<html>
  <head>
  ...
  <sj:Favicon runat="server" /> 
  ...

For testing, I found a favicon generator website out on the internet — I won’t share this since it’s appeared to be a little suspect — and created a smiley face favicon. I set this on my start item, and published:

smiley-favicon

After clearing it out on my start item, and publishing, the fallback Sitecore favicon appears:

sitecore-favicon

When you remove all favicons, none appear.

no-favicon

If you have any thoughts, suggestions, or comments on this, please share below.

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