Vulnerability in EPiServer.Forms

Try our conversational search powered by Generative AI!

Linus Ekström
Oct 24, 2012
  13791
(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
Stop Managing Humans in Your CMS

Too many times, a content management system becomes a people management system. Meaning, an organization uses the CMS to manage all the information...

Deane Barker | Nov 30, 2023

A day in the life of an Optimizely Developer - Optimizely CMS 12: The advantages and considerations when exploring an upgrade

GRAHAM CARR - LEAD .NET DEVELOPER, 28 Nov 2023 In 2022, Optimizely released CMS 12 as part of its ongoing evolution of the platform to help provide...

Graham Carr | Nov 28, 2023

A day in the life of an Optimizely Developer - OptiUKNorth Meetup January 2024

It's time for another UK North Optimizely meet up! After the success of the last one, Ibrar Hussain (26) and Paul Gruffydd (Kin + Carta) will be...

Graham Carr | Nov 28, 2023

Publish content to Optimizely CMS using a custom GPT from OpenAI 🤖

Do you find the traditional editor interface complicated and cluttered? Would you like an editorial AI assistant you can chat with? You can!

Tomas Hensrud Gulla | Nov 28, 2023 | Syndicated blog

Optimizely Graph and Next.js: Building Scalable Headless Solutions

Optimizely Graph harnesses the capabilities of GraphQL, an intuitive and efficient query language to, transform content within an Optimizely CMS in...

Szymon Uryga | Nov 27, 2023

Getting Started with Optimizely SaaS Core and Next.js Integration: Testing Content Updates

The blog post discusses the challenges of content updates on a website using Optimizely CMS, Next.js, and the Apollo Client due to Apollo's local...

Francisco Quintanilla | Nov 27, 2023 | Syndicated blog