Taking Control of Property Rendering

by: Steve Celius

I answered a question in the developer forum today, about the span tags that the EPiServer:Property control renders when you pass it the name of a PropertyString property. The thing is; the Property control itself supports setting a CSS class, colors, borders and all the other things supported by the WebControl class in ASP.NET. So it needs to render a stylable container, hence the <span> tag.

So your:

<EPiServer:Property CssClass="MyClass" 
                    PropertyName="PageName" 
                    runat="server" />

will render:

<span class="MyClass">My Page Name</span>

If you remove the CssClass like this:

<EPiServer:Property PropertyName="PageName" 
                    runat="server" />

it will render:

<span>My Page Name</span>

but you really want it to render:

My Page Name

and nothing more. The experienced EPiServer developer will tell you that:

<%= CurrentPage["PageName"] %>
will do exactly that. Yes - true, but what if you've got lots of these Property controls? Or they are inside repeaters or page lists, and you don't want to forget to databind anything. And - you don't want to lose the On Page Editing possibility.

There has to be a smart way to do this! And of course there is - duh - I'm blogging about it.

The new property architecture in CMS 5 is really powerful, and I'll show you how you can override the default rendering for any property type in the system.

In EPiServer 4, a property ultimately inherited from the PropertyData class, and could provide its own rendering by overriding the CreateChildControls method (you kind of had to - in order to do anything useful.)

In EPiServer CMS 5, the property data has been split from the rendering. You now have your PropertyData class, holding the data, and another class doing the rendering, which inherits from EPiServer.Web.PropertyControls.PropertyDataControl or any of the more specialized classes in the same namespace.

A PropertyData object can override the CreatePropertyControl() method, and return a PropertyDataControl that knows how to render the PropertyData in the correct way. It will default to the PropertyStringControl class, which will render a span tag in view mode, and a simple textbox in edit mode.

The Property web control uses a different approach. It uses the EPiServer.Core.PropertyControlClassFactory class to retrieve the PropertyDataControl class. It has a table of mapped PropertyData to PropertyDataControl types, which gives us the possibility to change the control class on the fly. (If there is no mapping, it will call the PropertyData.CreatePropertyControl() method.)

So - lets make a better PropertyStringControl class. Add the following code to your project:

/// <summary>
/// A no nonsense renderer for PropertyString values. Will
/// not render a span tag around the content (as the default)
/// implementation will if not needed by attributes set on
/// the Property control.
/// </summary>
/// <remarks>
/// The control supports On Page Editing (which will
/// render a span tag as long as you do the editing, and
/// after the submit postback).
/// Note! If you set any style attributes or the CSS class
/// on the Property control in the markup it will render
/// the text enclosed in a span tag.
/// </remarks>
public class PropertyStringExControl : PropertyStringControl
{
    public override void CreateDefaultControls()
    {
        bool needContainer = false;
        if (AttributeSourceControl.ControlStyle.IsEmpty == false)
            needContainer = true;

        ITextControl target;
        if (needContainer == true)
        {
            // Default is a Label control, which will
            // render a span tag around the content.
            target = new Label();
            target.Text = this.ToWebString();
            // The label should get the styles, the
            // Literal will not.
            this.CopyWebAttributes((WebControl)target);
        }
        else
        {
            // The Literal will only render the text
            target = new Literal();
            target.Text = this.ToWebString();
        }

        this.Controls.Add((Control)target);
    }
}

We inherit from the PropertyStringControl, and the only method we need to change is the CreateDefaultControls. This shows how powerful the new property architecture is.

The code above checks to see if you have put any CssClass or style attributes on the Property control in your markup. In that case, you will get a span tag. If not, we will only render the text itself, without the enclosing span tag.

When entering On Page Editing mode, the CreateOnPageEditControls method will be called instead of our CreateDefaultControls. And the default behaviour fits us just fine (because it works.) As a side note, if you'd like to change the edit mode behaviour, override the CreateEditControls method.

The hard work is done, all we need to do now is tell EPiServer about it. Open global.asax.cs, and make sure your Application_Start looks something like this:

protected void Application_Start(Object sender, EventArgs e)
{
    PropertyControlClassFactory.Instance.RegisterClass(
        typeof(PropertyString), typeof(PropertyStringExControl));
}

This will register the PropertyStringExControl as the default render control for all PropertyString properties you've got.

So, there you go. You can now control how any property type will render when using the Property web control.

28 February 2008


Comments

  1. A nice article Steve. I would like to add some points to it though:- 1. You / we manage to by pass the normal rendering of EPiServer:Property tag by using the concept defined in your article here but still to an extent it is limitd. 2. I mean - if somebody wants to render a particular control / type for a limited set of properties and for the rest the default (i.e. a Literal()) then there has to be a mechanism.. 3. ENTER Design Pattern Factory : I am not sure but if we can parameterize our class ( in constructor may be) PropertyStringExControl() then we can instanicate our own custom class which in turn would render a specifc web / HTML control. and that's how we can make it truely dynamic by employing the Factory design pattern (Which only asks the developer to spcify his wish of clsss and instantiates the corresponding class thus introducing de-coupling at its best) Your thoughts Steve Thanks
Post a comment    
User verification Image for user verification  
Steve Celius

About me

I work for EPiServer in Norway, mostly with technical stuff. Trying to keep up with all the new stuff from the development team. I also hang out on the EPiCode project, why don't you come join us?


Syndications


Archive


Tag cloud

EPiTrace logger