Custom properties in EPiServer using user controls

This article was migrated from an older iteration of our website, and it could deviate in design and functionality.


EPiServer Template Foundation makes it a lot easier to create custom properties based on user controls. In this post we go through the steps needed to create a new custom property in EPiServer and use user controls for rendering the property in edit mode and on page templates.

Estimated read time : 15 minutes

Jump to

Do you know the basics of an EPiServer property?

Before you start developing your own custom properties you should be familiar with the fundamentals of EPiServer properties. I’ve put together a post with an introduction to EPiServer properties that you should look through if you’re not already familiar with them.

Prerequisites

First of all, we use a series of classes available in EPiServer Template Foundation that help us create strongly typed custom properties that use user controls for the different property controls.

EPiServer Template Foundation, or ETF for short, is available as an open-source project on CodePlex (note that it requires EPiServer 6 and Page Type Builder).

If you want an introduction to EPiServer Template Foundation you can check out the introduction to Template Foundation, or have a look at how to set up a website from scratch with ETF.

What we want to do

We will build a custom property for “slide shows”, similar to the one we have on our start page:

image

Here are the basic user stories for the custom property:

As an editor, I want to be able to…

  1. …manage a list of slides for a slide show
  2. …select image, link, and text for each slide
  3. …rearrange added slides

As a developer, I want to be able to…

  1. …insert the slideshow on a page template
  2. …easily access the different slide objects
  3. …easily modify the property value (ie the slides in the slide show)

Proposed solution

  1. Create a custom property called PropertySlides
  2. Create business objects for SlideItem and SlideItemCollection
  3. Create a user control for the user interface in edit mode
  4. Create a user control for rendering the slideshow on a page template

Create the business objects

First we create the business objects required to work with the slide show content. We call them SlideItem and SlideItemCollection:

Business objects in Solution Explorer

Create the SlideItem class

The SlideItem class is fairly trivial:

[Serializable]
[XmlRoot(ElementName = "Slide")]
public class SlideItem
{
    [XmlElement(ElementName = "LinkText")]
    public string LinkText { get; set; }
 
    [XmlElement(ElementName = "Link")]
    public string LinkUrl { get; set; }
 
    [XmlElement(ElementName = "Image")]
    public string ImageUrl { get; set; }
 
    [XmlElement(ElementName = "Text")]
    public string SlideText { get; set; }
}

Note that we make the class serializable by adding the Serializable attribute, and we also use the XmlElement attribute to control what the resulting XML elements should be named.

Create the SlideItemCollection class

Note that you generally shouldn’t inherit from a concrete implementation when creating collections, instead the class should inherit an interface such as IEnumerable or ICollection. However, for the sake of brevity we’ll inherit straight from List in this case:

[Serializable]
[XmlRoot(ElementName = "Slides")]
public class SlideItemCollection : List<SlideItem>
{
}

Create the custom property type

Create a new class called PropertySlides and make it inherit from UserControlPropertyBase:

public class PropertySlides : UserControlPropertyBase<SlideItemCollection> { }

Next we add the PageDefinitionTypePlugIn attribute to set the name and description of the property:

[PageDefinitionTypePlugIn(
    DisplayName = "Slides", 
    Description = "Used to edit content for slides")]
public class PropertySlides : UserControlPropertyBase<SlideItemCollection> { }

That gives us a strongly typed property (note that we set SlideItemCollection as our property value type). The only requirement for the value type is that it’s a type which is XML serializable. Remember how we added the Serializable attribute to the SlideItemCollection class earlier?

Specify which user controls to use

Next we need to specify the property controls to use for the rendering modes we want to support: Edit, Default (optional), and OnPageEdit (optional).

We do this by adding the UserControlProperty attribute:

[PageDefinitionTypePlugIn(
    DisplayName = "Slides", 
    Description = "Used to edit content for slides")]
[UserControlProperty(
    EditUserControlPath = "/Templates/Properties/Slides/PropertySlidesControl.ascx",
    DefaultUserControlPath = "/Templates/UserControls/SlideShow.ascx")] 
public class PropertySlides : UserControlPropertyBase<SlideItemCollection> { }

If we later want to support on-page editing we can simply add the OnPageEditUserControlPath parameter:

[UserControlProperty(
   EditUserControlPath = "/Templates/Properties/Slides/PropertySlidesControl.ascx",
   DefaultUserControlPath = "/Templates/UserControls/SlideShow.ascx",
   OnPageEditUserControlPath = "/Virtual/Path/To/OnPageEditUserControl.ascx")] 
public class PropertySlides : UserControlPropertyBase<SlideItemCollection> { }

Now we add the property to one of our page types so that we can test it:

image

Ok, that’s all we need for our property type, next we’ll create the user interface by implementing the two user controls we specified.

Create the custom property user interface

Next we’ll create the user interface for editing the property. Create a new user control called PropertySlidesControl

image

and make it inherit PropertyUserControlBase:

public partial class PropertySlidesControl : PropertyUserControlBase<SlideItemCollection> { }

Note that you need to specify the same value type for the user control by setting the type parameter to SlideItemCollection.

Next we’ll add some controls to our user control to allow editors to select the link, link text, image, and HTML content for each slide:

<ETF:PageSelector Name="Link" ID="targetLink" runat="server" />
<ETF:TextBox Name="Link text" Description="The text to display as the link to the target page" ID="linkText" runat="server" />
<ETF:ImageSelector Name="Image" ID="slideImage" runat="server" />
<ETF:TextBox Name="Slide text" Description="The text to display on top of the slide background image" TextMode="MultiLine" ID="slideText" runat="server" />
<ETF:ToolButton ButtonType="Add" OnClick="btnAddSlide_Click" Indented="true" Text="Add slide" runat="server" />

We also need some controls to enable us to change the slides order. We’ll add a listbox and a couple of buttons to move slides up and down:

<ETF:ListBox Name="Slides" Description="A list of added slides" ID="lstSlides" runat="server" />
<ETF:PagePropertyStyleControl runat="server">
    <ControlsTemplate>
        <ETF:ToolButton ButtonType="Down" Text="Down" OnClick="btnMoveDown_Click" runat="server" />
        <ETF:ToolButton ButtonType="Up" Text="Up" OnClick="btnMoveUp_Click" runat="server" />
        <ETF:ToolButton ButtonType="Delete" Text="Remove" OnClick="btnRemoveSlide_Click" runat="server" />
    </ControlsTemplate>
</ETF:PagePropertyStyleControl>

Note that we use web controls that are part of EPiServer Template Foundation to include EPiServer controls in our user interface:

image

In order to use the web controls for the UI we need to add a tag prefix for the TemplateFoundation.UI.WebControls namespace in web.config:

<add tagPrefix="ETF" namespace="TemplateFoundation.UI.WebControls" assembly="TemplateFoundation"/>

image

We want to show all added slides when the user control loads, so we’ll add the following to populate the listbox:

/// <summary>
/// Populate the listbox with added slides
/// </summary>
protected void PopulateListBox()
{
    // Verify the property value isn't null
    if (Value==null)
    {
        return;
    }
 
    lstSlides.Items.Clear();
 
    try
    {
        // Iterate all slides in the slideshow (the Value property is of type SlideItemCollection)
        foreach (var slide in Value)
        {
            var pageLink = PageReference.ParseUrl(slide.LinkUrl);
 
            lstSlides.Items.Add(string.Format("{0} [\"{1}\"]", slide.LinkText, pageLink.GetPage().PageName));
        }
    }
    catch
    {
        Controls.Add(new ValidationErrorMessage() { Text = "An error occured in the slides property" });
    }
}

When we click the Add slide button we want to create a SlideItem object and add it to the SlideItemCollection:

protected void btnAddSlide_Click(object sender, EventArgs e)
{
    if(Value == null)
    {
        Value = new SlideItemCollection();
    }
 
    var item = new SlideItem()
                   {
                       ImageUrl = slideImage.SelectedImageUrl,
                       LinkText = linkText.Text,
                       SlideText = slideText.Text,
                       LinkUrl = targetLink.SelectedPage.GetPage().LinkURL
                   };
 
    // Add the SlideItem to the SlideItemCollection
    Value.Add(item);
 
    // Refresh the listbox
    PopulateListBox();
}

Non language-specific properties

If “Unique value per language” has been disabled, all controls will be disabled if a language version other than the master language version is being edited:

image

Create the default controls

Let’s create the default controls, the user control we specified with the UserControlProperty attribute earlier, used to render the property on page templates:

image

Create a user control called SlideShow and make it inherit PropertyUserControlBase. Make sure it uses the same value type as before:

public partial class SlideShow : PropertyUserControlBase<SlideItemCollection> { }

Let’s add a Repeater control for the slide show markup:

<asp:Repeater ID="rptSlides" runat="server">
    <ItemTemplate>
        <div class="slide">
            <img src="<%# Eval("ImageUrl") %>?w=900&ch=305" alt="" />
            <p class="text">< %# Eval("SlideText") %></p>
            <p><a class="slide-link" href="<%# Eval("LinkUrl") %>">< %# Eval("LinkText") %></a></p>
        </div>
    </ItemTemplate>
</asp:Repeater>

Now let’s databind the Repeater to the property value, in this case the SlideItemCollection object:

protected override void OnPropertyLoad(EditPageEventArgs e)
{
 
   base.OnPropertyLoad(e);
 
   if (Value!=null)
   {
      rptSlides.DataSource = Value;
      rptSlides.DataBind();
   }
}    

Now all we have to do in order to include the slide show on a page template is to insert an EPiServer Property control:

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

Saving and loading the property value

The PropertyUserControlBase has two events called PropertyLoad and PropertySave designed to make it easier to know when to get and set the Value property. Here’s a brief post on loading and saving a custom property value in a property user control.