Go Back

Making Control Editing User-Friendly with Sitefinity Control Designers

Sitefinity ASP.NET CMS can be customized very easily using traditional ASP.NET technologies.  This makes it possible for .NET developers to create new functionality by adding tools.  These tools can then be used by editors (non-developers) to manage the web site. 

By default, Sitefinity will automatically generate a web-based interface for custom controls/tools.  For very simple controls, this interface is probably sufficient.  For complex controls, this interface can quickly become overwhelming. 

Sitefinity - This is NOT user friendly

The interface above fails to empower editors to manage the web site; it isn't user-friendly and will intimidate non-developers.  To remedy this, developers can use Control Designers to replace the default web-interface with a very user-friendly interface. 

Sitefinity User Friendly Control Designer

Creating a Custom Tool using an ASP.NET UserControl

Developers will primarily extend Sitefinity through ASP.NET Controls.  Below is a basic UserControl that displays Text with varying emphasis based on the Priority level.  (See code notes below.

~/App_Code/CustomControlBase.cs

[Telerik.Framework.Web.Design.ControlDesigner("CustomDesigner")]
public class CustomControlBase : System.Web.UI.UserControl
{
    public string Text { get; set; }

    public PriorityType Priority { get; set; }

    public enum PriorityType
    {
        Low,
        Medium,
        High
    }
}

~/Custom/UserControls/CustomControl.ascx

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="CustomControl.ascx.cs" Inherits="Custom_UserControls_CustomControl" %>
<p><asp:Label ID="Label1" runat="server" /></p>

~/Custom/UserControls/CustomControl.ascx.cs

using System;

public partial class Custom_UserControls_CustomControl : CustomControlBase
{
    private const string low = "font-size: 10px;";
    private const string medium = "font-size: 20px;";
    private const string high = "font-size: 30px;";

    protected void Page_Load(object sender, EventArgs e)
    {
        Label1.Text = Text;

        switch (Priority)
        {
            case PriorityType.Low:
                Label1.Style.Value = low;
                break;
            case PriorityType.Medium:
                Label1.Style.Value = medium;
                break;
            case PriorityType.High:
                Label1.Style.Value = high;
                break;
        }
    }
}

Add a Sitefinity tool mapping to this UserControl in the ~/web.config file:

<configuration>
  <telerik>
    <cms>
      <toolboxControls>
        <add name="Custom Control" section="My Controls" url="~/Custom/UserControls/CustomControl.ascx" />
      </toolboxControls>
    </cms>
  </telerik>
</configuration>

Code Notes

  • The CustomControlBase class facilitates the Control Designer's access to this control's properties.  (more information)
  • The UserControl inherits from the CustomControlBase class instead of the UserControl class.
  • The CustomControlBase class inherits from the UserControl class. 
  • The Control Designer is attached to this control using the ControlDesigner attribute. (more information)
  • The Control Designer referenced by the ControlDesigner attribute above does not yet exist; we'll create it in the step below.

Sitefinity Custom Control in the Page Editor

Creating a Custom Control Designer

In the step above a new UserControl and Control Base class was added to Sitefinity.  In addition, a Control Designer was attached to the UserControl.  In this step, we'll create the Control Designer and replace the default auto-generated interface with a user-friendly custom interface.

~/App_Code/CustomDesigner.cs

using System;
using System.Web.UI.WebControls;

public class CustomDesigner : Telerik.Framework.Web.Design.ControlDesigner
{
    /// <summary>
    /// Loads an external template that contains the controls & UI for the Control Designer.
    /// </summary>
    public override string LayoutTemplatePath
    {
        get { return "~/Custom/Admin/ControlDesigners/CustomDesigner.ascx"; }
    }

    /// <summary>
    /// Executed when Control Designer is initialized.
    /// </summary>
    protected override void InitializeControls(System.Web.UI.Control viewContainer)
    {
        // Populate drop-down and preset textbox and dropdownlist if we're not in a postback.
        if (!Page.IsPostBack)
        {
            TextBox1.Text = ((CustomControlBase) DesignedControl).Text;
            DropDownList1.DataSource = Enum.GetNames(typeof (CustomControlBase.PriorityType));
            DropDownList1.DataBind();

            ListItem item = DropDownList1.Items.FindByText(((CustomControlBase) DesignedControl).Priority.ToString());
            if (item != null)
            {
                DropDownList1.SelectedIndex = DropDownList1.Items.IndexOf(item);
            }
        }
    }

    /// <summary>
    /// Executed automatically when the I'm done button is clicked in the Control Designer.
    /// </summary>
    public override void OnSaving()
    {
        // Set the underlying control properties to the new values.
        ((CustomControlBase) DesignedControl).Text = TextBox1.Text;
        ((CustomControlBase) DesignedControl).Priority = 
            (CustomControlBase.PriorityType) Enum.Parse(typeof(CustomControlBase.PriorityType), DropDownList1.SelectedValue);
    }

    /// <summary>
    /// Gets a reference to the TextBox control contained in the LayoutTemplatePath template.
    /// </summary>
    protected virtual TextBox TextBox1
    {
        get { return base.Container.GetControl<TextBox>("TextBox1", true); }
    }

    /// <summary>
    /// Gets a reference to the DropDownList control contained in the LayoutTemplatePath template.
    /// </summary>
    protected virtual DropDownList DropDownList1
    {
        get { return base.Container.GetControl<DropDownList>("DropDownList1", true); }
    }
}

~/Custom/Admin/ControlTemplate/CustomDesigner.ascx

<%@ Control Language="C#" %>

<div class="ctrlProps">
    <div class="ctrlContent">
        <h3>Text to be displayed</h3>
        <p class="news-location">
            <asp:Label AssociatedControlID="TextBox1" Text="Enter text below:" EnableViewState="false" runat="server" />
            <asp:TextBox ID="TextBox1" Columns="40" runat="server" />
        </p>
        <p>
            <asp:Label AssociatedControlID="DropDownList1" Text="Priority:" EnableViewState="false" runat="server" />
            <asp:DropDownList ID="DropDownList1" runat="server">
            </asp:DropDownList>
        </p>
    </div>
</div>

Code Notes

  • LayoutTemplatePath can be used to automatically load an external template.
  • InitializeControls is executed automatically when the Control Designer is loaded.  This is a great place to preset web control values based on current control settings.
  • OnSaving is executed automatically when the I'm done button is clicked in the Control Designer.
  • There is no code-behind for the CustomDesigner.ascx template (UserControl).  You can create a code-behind file if you wish, but it's probably better to put your code-behind logic in the CustomDesigner class.

Conclusion

To get the most out of Sitefinity developers need to empower non-developers (marketing, administrators, editors, etc) to manage the web site.  Control Designers allow developers to do this by creating user-friendly admin. interfaces for their tools.

Comments  5

  • Lesley-Ann 12 Mar

    hi there tried to use your example to get to grips with this functionality (evaluating Sitefinity for a new site). InitializeControls code is only called the 1st time (not on postbacks), and so "OnSaving" being called, both the text and the dropdown have no data. similarly - moving between the basic and advanced tab - there is no data in text/dropdown on moving back. I fixed it by removing the "IsPostback" check in initialise controls, so that the state is initialised every time. Is this the right way to fix it? Or has something else gone wrong in my version, eg with viewstate? "enableviewstate" is not on on the container... Thanks, Lesley-An
  • Lesley-Ann 12 Mar

    hi there tried to use your example to get to grips with this functionality (evaluating Sitefinity for a new site). InitializeControls code is only called the 1st time (not on postbacks), and so "OnSaving" being called, both the text and the dropdown have no data. similarly - moving between the basic and advanced tab - there is no data in text/dropdown on moving back. I fixed it by removing the "IsPostback" check in initialise controls, so that the state is initialised every time. Is this the right way to fix it? Or has something else gone wrong in my version, eg with viewstate? "enableviewstate" is not on on the container... Thanks, Lesley-An
  • Romi 31 Mar

    Hi Gabe, Some way to add in the Dropdownlist the listofNews ( Title and ID) to use as selector for SingleNews. Regards
  • Sara 09 Jul

    Is this still valid in 3.6 and beyond?
  • Slawek 22 Oct

    Hi,

    Thanks for this article it helped a lot! I've created a product gallery control with 8 images and it works great.

    I want to make it a bit better now. In my ProductGalleryBase.cs I declare 16 strings 2x8 for image file name and image alt tag. Then in ProductGallery.ascs.cs I assign 5 properties for each image & hyperlink control. I can't find a way to do it in a loop which would be faster and cleaner way.

    If I declare 2 string arrays in ProductGalleryBase.cs will it work the same?

    Thanks,
    Slawek
Post a comment!
  1. Formatting options
       
     
     
     
     
       
  2. I'm sorry for the CAPTCHA. You have spammers to thank for this: