codeflood logo

Setting incompatible properties from parameters

Sitecore enables us to create reusable presentation components by allowing us to pass parameters to our component when it's used. Rather than hard coding everything required by the components, or reading it from some predefined location in the content tree, we can instead set the parameters of the component when it's placed on the page either through the presentation dialog or in the page editor.

Parameters Dialog

We can provide the parameters using the "Additional Parameters" name value list at the bottom of the dialog, but this requires us to remember exactly the name of each parameter and manually enter the value, whether that be plain text (nice and easy to enter that) or an ID of an item somewhere in the content tree (would be a lot nicer to use a Sitecore field to select that one). A more user friendly approach is to define a Parameters Template for the component which provides a field for each settable parameter. A Parameters Template is a template which contains /sitecore/templates/System/Layout/Rendering Parameters/Standard Rendering Parameters as a base template and it's assigned to the component through the Parameters Template field on the presentation component definition item (the sublayout, rendering or view rendering). Now when the component properties are edited in the presentation dialog, the Parameters Template is used to display fields to the user which they can fill in.

Sitecore provides these parameters to the component in a different way depending on what type the component is. XSLT Renderings are the luckiest components as Sitecore will pass each parameter as a parameter to the XSLT. The remaining components however must parse the parameters manually.

If you're using WebForms there is a fantastic utility on the Sitecore Marketplace to help with setting parameters on sublayouts. The Sub layout Parameter Helper (I'm sure that was a typo) written by John West is a code snippet which can be included in your project. The code provides two classes. The helper class which does the heavy lifting of setting the parameters of your class and a SublayoutBase class to inherit the code behind class from which is all wired up already. The SublayoutBase class also exposes (and populates) properties for the configured data source and data source item. So if you want to create DMS ready components, you'll want to be using this module.

Let's take a look at this in action. I have the following sublayout which will be used to list out a set of links to related pages on the site. The component allows optionally setting a title and a "Read More" link which will appear at the bottom.

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="Links.ascx.cs" 
    Inherits="Sitecore7Sandbox.Layouts.Links" %>
<%@ Import Namespace="Sitecore.Links" %>

<% if (ShowTitle) { %>
<h2><%= Title %></h2>
<% } %>
<ul>
    <% foreach (var item in GetItems()){ %>
    <li>
        <a href="<%= LinkManager.GetItemUrl(item) %>">
            <%= item.Name %>
        </a>
    </li>
    <% } %>
</ul>
<% if (ReadMoreLink != Sitecore.Data.ID.Null) { %>
    <a href="<%= LinkManager.GetItemUrl(ReadMoreLinkItem) %>">
        <%= ReadMoreLinkItem.Name %>
    </a>
<% } %>

(Geez, doesn't that WebForms markup look ugly once you've been using MVC and Razor...)

Code Behind

using System.ComponentModel;
using Sandbox;
using Sitecore.Data;
using Sitecore.Data.Fields;
using Sitecore.Data.Items;
using Sitecore.Sharedsource.Web.UI.Sublayouts;

namespace Sitecore7Sandbox.Layouts
{
    public partial class Links : SublayoutBase
    {
        public string Title
        {
            get;
            set;
        }

        public string FieldName
        {
            get;
            set;
        }

        public bool ShowTitle
        {
            get;
            set;
        }

        public ID ReadMoreLink
        {
            get;
            set;
        }

        public Item ReadMoreLinkItem
        {
            get
            {
                if (ReadMoreLink != Sitecore.Data.ID.Null)
                    return Sitecore.Context.Database.GetItem(ReadMoreLink);

                return null;
            }
        }

        public Item[] GetItems()
        {
            if (DataSourceItem != null && DataSourceItem.Fields[FieldName] != null)
            {
                var field = (MultilistField) DataSourceItem.Fields[FieldName];
                return field.GetItems();
            }

            return new Item[0];
        }
    }
}

Note there that the code behind class inherits from SublayoutBase so the properties of my class get populated from the parameters provided by Sitecore, matching them on name.

Now let's place this component on a page and setup some parameters like so:

Configured Parameters

And now we hit the page and...

Error due to incompatible parameter types

...and we hit a wall. "String was not recognized as a valid Boolean". The issue is that the parameters are passed as string values which are not compatible with some of my parameter types. ShowTitle is a boolean and ReadMoreLink is an ID, both of which cannot be converted to from a string. If my ReadMoreLink had been listed first in my parameters then I may have also got the error "Invalid cast from 'System.String' to 'Sitecore.Data.ID'.".

Now before you go off and change all your parameters to only accept strings, we do have another option. The Sitecore reflection utilities which are used by the sublayout parameter helper allow defining a type converter on each property being set. The reflection utilities will use the type converter to convert from the string value into the required target type.

So to make the above solution work, we need a converter for a bool value and one for an ID.

public class SitecoreBooleanConverter : TypeConverter
{
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        return sourceType == typeof (string);
    }

    public override object ConvertFrom(ITypeDescriptorContext context, 
        System.Globalization.CultureInfo culture, object value)
    {
        return (value as string) == "1";
    }
}

public class SitecoreIDConverter : TypeConverter
{
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        return sourceType == typeof (string);
    }

    public override object ConvertFrom(ITypeDescriptorContext context, 
        System.Globalization.CultureInfo culture, object value)
    {
        var strValue = value as string;
        if (string.IsNullOrEmpty(strValue))
            return ID.Null;

        return new ID(value as string);
    }
}

The type converters simply take the string value and convert it into the required target type.

We can now apply the type converters to the troublesome properties by adding them as attributes on the parameter:

[TypeConverter(typeof(SitecoreBooleanConverter))]
public bool ShowTitle
{
    get;
    set;
}

[TypeConverter(typeof(SitecoreIDConverter))]
public ID ReadMoreLink
{
    get;
    set;
}

And now when we hit our page, everything works as expected.

working

Comments

Thanks for the write-up here. This biggest reason to avoid rendering parameters and instead opt for a data source item else where in the tree is that the parameters are stored on the page's "__Renderings" field which is Shared, and therefore will not support multi-lingual sites. For this reason, they should be used for basic options of the component and not content, e.g. a checkbox to show something, or a max count for a list, but not a title heading or any type of content.

Alistair Deneys

Good point Mark, Yes, I suppose you do highlight the weakness of my example :) But you have a valid point there, the parameter settings should be more about the behaviours of the control and never about the content it displays. Also consider DMS and personalisation. Drawing the content from a data source is much easier to orchestrate than trying to change parameters through a rules engine rule.

Great article and an informative insight for new users of Sitecore or even oldies such as myself. I would agree with what mursino is describing but you need to think of the following benefits you are wanting to incorporate into any component before making any decisions; - DMS / Personalisation friendly - Re-usable components with completely different data on the same page (modularity, my fav word for the moment) - version control of data - Page editor friendly including previewing - multi lingual data
+ many more...
I only ever use parameters one as datasource and two as additional caching options (have custom caching features) or unique global settings for the component across versions and languages.

Leave a comment

All fields are required.