linuse
Oct 24, 2012
  13005
(5 votes)

Custom renderers for properties

In my previous blog post about updates to typed models in EPiServer 7 I mentioned that we have added the possibility to select renderer for properties, pretty much the same way as you can assign renderers for blocks and pages. Let’s look a bit more into how this works. We begin with defining a simple page type with a single property:

using System.ComponentModel.DataAnnotations;
using EPiServer.Core;
using EPiServer.DataAnnotations;
using EPiServer.Web;
 
namespace EPiServer.Templates.Alloy.Models.Pages
{
    [ContentType(GUID = "F8D47655-7B50-4319-8646-3369BA9AF05E")]
    public class MyPage : PageData
    {
        [UIHint("email")]
        public virtual string Email { get; set; }
    }
}

Let’s create a simple renderer for the property. We’ll start by creating a property that inherits from one of the built in Property Controls: PropertyStringControl.

using System.Web.UI.WebControls;
using EPiServer.Framework.DataAnnotations;
using EPiServer.Web.PropertyControls;
using EPiServer.Web;
 
[TemplateDescriptor(TagString = "email")]
public class EmailPropertyControl : PropertyStringControl, IRenderTemplate<string>
{
    public override void CreateDefaultControls()
    {
        var link = new HyperLink();
        link.Text = PropertyData.Value.ToString();
        link.NavigateUrl = "mailto:" + PropertyData.Value.ToString();
        Controls.Add(link);
    }
}

This class is very similar to what you would do in EPiServer CMS 5 and 6. There are two differences though. The first is that it implements the IRenderTemplate<T> interface. The second is the TemplateDescriptor attribute. This tells the system that this editor is preferred when rendering a model property with a UIHint attribute that matches the TemplateDescriptor Tag/TagString properties(Tag and TagString are basically the same property but with different types. Tags is defined as a string array which is not possible if you have an CLS-compliant project).

In our page template we add a standard EPiServer property web control to display the property:

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

When viewing a page of this type we get a simple a tag with a mailto-link:

SimpleProperty

Defining tags in the template

In the first example we added a UIHint attribute to our model. But what if we want to display the model differently in different templates? This can be done by assigning a tag in the RenderSettings property of the EPiServer property web control. Let us assume that we are working for a fictitious client that imports bananas. Their web strategists have decided that they should have a lot of images of bananas on their web site and what better way to implement this than with the great dancing banana image. Lets add a tag to the property:

<EPiServer:Property PropertyName="Email" runat="server" />
    <RenderSettings Tag="emailgoesbananas" />
</EPiServer:Property>

And we’ll create a new renderer that will add a dancing banana next to the mail link:

 
using System.Web.UI.WebControls;
using EPiServer.Framework.DataAnnotations;
using EPiServer.Web.PropertyControls;
using EPiServer.Web;
 
[TemplateDescriptor(TagString = "emailgoesbananas")]
public class BananaEmailPropertyControl
     : PropertyStringControl, IRenderTemplate<string>
{
    public override void CreateDefaultControls()
    {
        var link = new HyperLink();
        link.Text = PropertyData.Value.ToString();
        link.NavigateUrl = "mailto:" + PropertyData.Value.ToString();
        Controls.Add(link);
 
        Controls.Add(new Image() 
        { ImageUrl = "http://www.sherv.net/cm/emo/funny/2/banana.gif" });
    }
}

DancingBananaScreenShot

And behold: We have a dancing banana!

Using a user control to render the property

Another new feature is the ability to define a user control to render your property. To do this you have to inherit from the generic class PropertyControlBase:

using EPiServer.Framework.DataAnnotations;
using EPiServer.Web;
 
namespace EPiServer.Templates.Alloy.Views.Pages.Partials
{
    [TemplateDescriptor(TagString = "bananasgoesbananas")]
    public partial class EvenMoreBananas : PropertyControlBase<string>
    {
    }
}

Let’s say the customer was thrilled with the dancing banana and just want more. To speed up the development we’ll add these to the user control instead:

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="EvenMoreBananas.ascx.cs"
    Inherits="EPiServer.Templates.Alloy.Views.Pages.Partials.EvenMoreBananas" %>
<asp:Panel runat="server" ID="BananaWrapper">
    <a href='mailto:<%=CurrentData %>'>
        <%
   1: =CurrentData 
%></a>
    <img src="http://www.sherv.net/cm/emo/funny/2/banana.gif" />
    <img src="http://www.sherv.net/cm/emo/funny/2/upside-down-banana-smiley-emoticon.gif" />
    <img src="http://www.sherv.net/cm/emo/funny/2/banana-with-bagpipes-smiley-emoticon.gif" />
    <img src="http://www.sherv.net/cm/emo/funny/2/woohoo-dancing-banana-smiley-emoticon.gif" />
</asp:Panel>

Note: CurrentData in this case is our model value typed as a string as defined in our inheritance declaration.

We change the tag in our page template to the one defined in the user control, bananasgoesbananas, and reloading the page gives us:

ManyDancingBananas

When clicking on the email text we still get the default editing (inline editing for strings). This is because the user control is wrapped in a generic property control that handles editing attributes so that you don’t have to care about this.

EditingEmailString

Sum up

With these additions working with complex models, like pages and blocks, and simple properties become pretty much the same. You can:

  • Register renderers for them using the TemplateDescriptor attribute.
  • Select renderer using the UIHint attribute..
  • …or by defining a tag in RenderSettings Property for the EPiServer property web control.

If no renderer is found given the specified tags PropertyControlClassFactory will fall back to the current behavior.

Oct 24, 2012

Comments

Björn Olsson
Björn Olsson Oct 24, 2012 10:32 AM

This looks very promising, it would be nice if it was possible to pass custom variables to the renderer using the RenderSettings (And UI hint perhaps). Let's say you would like to implement a "DateTime-renderer", with a customizable date-format, which would go something like this:





Or...





Etc..

You digg? :)

Oct 24, 2012 01:23 PM

Björn: You can add any attribute to the RenderSettings property and you can access them in your property control like this:
var customVariable = RenderSettings["CustomVariable"];

This property is currently only accessible for if you create a class derived from PropertyData and not when creating a user control as a renderer.

Björn Olsson
Björn Olsson Oct 24, 2012 01:46 PM

Sweet, sounds perfect.

akeegan@adagetechnologies.com
akeegan@adagetechnologies.com Jan 24, 2014 03:22 AM

Hello, I am currently attempting something similar to Björn in that I would like to render a DateTime property using a specific format. Unfortunately, even when I create a control which inherits from PropertyDateControl and implements IRenderTemplate, it doesn't seem to do anything.

I've tagged the DateTime property via its RenderSettings, but I'm still not seeing any effect. Is there something I am doing wrong?

Here is my property code:

[TemplateDescriptor(Tags = new string[] { Constants.RenderingTagNames.DateTime })]
public class FormattedPropertyDateControl : PropertyDateControl, IRenderTemplate
{
public override void CreateDefaultControls()
{
Label lblDateTime = new Label();
lblDateTime.Text = Date.ToString(RenderSettings["DateFormat"].ToString());
Controls.Add(lblDateTime);
}
}

Jan 29, 2014 11:16 AM

I tested your code and found out that the property data type in this case is defined as Nullable. Perhaps a bit confusing since I guess that the typed model uses DateTime but changing to IRenderTemplate> should make the control active.

Please login to comment.
Latest blogs
Customizing Property Lists in Optimizely CMS

Generic property lists is a cool editorial feature that has gained a lot of popularity - in spite of still being unsupported (officially). But if y...

Allan Thraen | Oct 2, 2022 | Syndicated blog

Content Delivery API – The Case of the Duplicate API Refresh Token

Creating a custom refresh provider to resolve the issues with duplicate tokens in the DXC The post Content Delivery API – The Case of the Duplicate...

David Lewis | Sep 29, 2022 | Syndicated blog

New Optimizely certifications - register for beta testing before November 1st

In January 2023, Optimizely is making updates to the current versions of our certification exams to make sure that each exam covers the necessary...

Jamilia Buzurukova | Sep 28, 2022

Optimizely community meetup - Sept 29 (virtual + Melbourne)

Super excited to be presenting this Thursday the 29th of September at the Optimizely community meetup. For the full details and RSVP's see the...

Ynze | Sep 27, 2022 | Syndicated blog