Create a custom property type

In most cases the built in property types are sufficient, but sometimes you'll need to add your own. Luckily that's pretty easy. You have two options, either to create a composite property type that aggregates property types or to create a completely customized property type with it's own editor.

Creating a composite property type

Previously creating new property types that combined other property types into one was a quite complex task since it also required an editor to be manually created that includes functionality for all those types that was included. To make this a lot easier a new composite property type has been added. When inheriting this property type you can build your custom property type using all availabe property types, combining for instance images with text and links, and the CMS will load the appropriate editor.

This is very useful for creating things like feature properties in conjunction with the collection property type to allow the editor to create a dynamic amount of features for something like a start page slider.

This is a sample that creates a new property type that has a heading, an HTML-block and a link. This is done by implementing properties using the property types StringProperty, HtmlProperty and LinkProperty:

namespace DemoSite.Business.PropertyTypes {
    using KalikoCMS.Attributes;
    using KalikoCMS.PropertyType;

    [PropertyType("9033A828-B49A-4A19-9C20-0F9BEBBD3273", "Feature", "Feature", EditorControl)]
    public class FeatureProperty : CompositeProperty {
        [Property("Header")]
        public StringProperty Header { get; set; }

        [Property("Feature body")]
        public HtmlProperty Description { get; set; }

        [Property("Featured link")]
        public LinkProperty Url { get; set; }

        // Override Preview with how to render items of this type in lists.
        // It's also possible to use more complex HTML-layout here if wanted.
        public override string Preview {
            get { return Header.Preview; }
        }
    }
}

All property type properties needs to be decorated using a PropertyAttribute in the same way as you do on page models.

No property editor needs to be created, by using the inherited EditorControl the CMS will put together the correct editors for you. The editor fields will be in the same order as they appear in your type, so if you want to reorder them just change the order of your properties.

Creating a custom property type

To add a property type we need to do two things. We need to create a class that defines our new property and we need to create an editor for it that will be displayed in the administration UI.

Lets start by adding the class that defines our new property type. We create a new class called FeatureProperty inside a new folder called PropertyType in our project. Any new property type needs to inherit from KalikoCMS.Core.PropertyData and we also need to add an attribute; KalikoCMS.Attributes.PropertyTypeAttribute.

The property type attribute needs a few parameters; a unique identifier (created by generating a Guid), a name, a description and a path to the editor control. Let's start by adding the attribute and the fields that we want our new property type to have as well as two constructors, one with our values and one empty (the empty is required). The fields our property require is a header, a description and an url. In our case these are all strings, but they could have been of any type.

namespace DemoSite.PropertyTypes {
    using KalikoCMS.Attributes;
    using KalikoCMS.Core;
    using KalikoCMS.Serialization;

    [PropertyType("9033A828-B49A-4A19-9C20-0F9BEBBD3273", "Feature", "Feature", "~/PropertyTypes/FeaturePropertyEditor.ascx")]
    public class FeatureProperty : PropertyData {
        public string Header { get; set; }
        public string Description { get; set; }
        public string Url { get; set; }

        public FeatureProperty() {
        }

        public FeatureProperty(string header, string description, string url) {
            Header = header;
            Description = description;
            Url = url;
        }
    }
}

Since we inherit from the base class PropertyData we need to implement a couple of members. These are used to serialize our data using JSON, calculating the checksum of an instance and also displaying the property.

namespace DemoSite.PropertyTypes {
    using KalikoCMS.Attributes;
    using KalikoCMS.Core;
    using KalikoCMS.Serialization;

    [PropertyType("9033A828-B49A-4A19-9C20-0F9BEBBD3273", "Feature", "Feature", "~/PropertyTypes/FeaturePropertyEditor.ascx")]
    public class FeatureProperty : PropertyData {
        private int? _cachedHashCode;

        public FeatureProperty() {
        }

        public FeatureProperty(string header, string description, string url) {
            Header = header;
            Description = description;
            Url = url;
        }

        public string Header { get; set; }
        public string Description { get; set; }
        public string Url { get; set; }

        // Needs to be implemented, displayed when accessing ToString() or preview
        protected override string StringValue {
            get { return string.Format("{0}", Header); }
        }

        // Needs to be implemented, just call DeserializeJson with your type
        protected override PropertyData DeserializeFromJson(string data) {
            return JsonSerialization.DeserializeJson<FeatureProperty>(data);
        }

        // Needs to be implemented, returns a hash for an instance of this type
        public override int GetHashCode() {
            return (int)(_cachedHashCode ?? (_cachedHashCode = CalculateHashCode()));
        }

        // Include all custom fields so that a correct hash is calculated
        private int CalculateHashCode() {
            int hash = JsonSerialization.GetNewHash();
            hash = JsonSerialization.CombineHashCode(hash, Header);
            hash = JsonSerialization.CombineHashCode(hash, Description);
            hash = JsonSerialization.CombineHashCode(hash, Url);
            return hash;
        }
    }
}

That's it for our property type class. This is typically the code you write for any new property type, the only main difference is the fields and the self referencing type in DeserializeFromJson.

Next step is to add the editor for our new property. In the PropertyTypes folder in your project add a new Web Forms User Control and name it FeaturePropertyEditor.ascx. We'll start by adding input controls for all our fields and a literal to show an error message if something goes wrong:

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="FeaturePropertyEditor.ascx.cs" Inherits="Kaliko.Web.CustomPropertyTypes.FeaturePropertyEditor" %>
<div class="form-group">
    <asp:Label AssociatedControlID="HeaderField" runat="server" ID="LabelText" CssClass="control-label col-xs-2" />
    <div class="controls col-xs-6">
      <div>
        <label for="<%=HeaderField.ClientID %>" class="control-label">Header:</label>
        <asp:TextBox runat="server" CssClass="form-control" ID="HeaderField" />
      </div>
      <div>
        <label for="<%=DescriptionField.ClientID %>" class="control-label">Description:</label>
        <asp:TextBox runat="server" CssClass="form-control" ID="DescriptionField" />
      </div>
      <div>
        <label for="<%=UrlField.ClientID %>" class="control-label">Url:</label>
        <asp:TextBox runat="server" CssClass="form-control" ID="UrlField" />
      </div>
      <asp:Literal ID="ErrorText" runat="server" />
    </div>
</div>

Switch to the code-behind and change so that the class inherits from KalikoCMS.PropertyType.PropertyEditorBase instead. This also require us to implement a couple of members. These are used for the system to send/get values from our control as well as validate input upon save. If we would add validation logic we could at the same place add an error to display in the ErrorText literal. Validation could be anything from that a field is required to more complex validation.

namespace Kaliko.Web.CustomPropertyTypes {
    using DemoSite.PropertyTypes;
    using KalikoCMS.Core;
    using KalikoCMS.PropertyType;

    public partial class FeaturePropertyEditor : PropertyEditorBase {
        // Used for the system to set a label when editing the propery
        public override string PropertyLabel {
            set { LabelText.Text = value; }
        }

        // Any validation logic goes here
        public override bool Validate() {
            return true;
        }

        // Any validation logic if property is required (or not) goes here
        public override bool Validate(bool required) {
            return true;
        }

        // Box and unbox our input fields to a FeatureProperty instance
        public override PropertyData PropertyValue {
            get {
                var property = new FeatureProperty {
                    Header = HeaderField.Text,
                    Description = DescriptionField.Text,
                    Url = UrlField.Text
                };
                return property;
            }
            set {
                var property = (FeatureProperty)value;
                HeaderField.Text = property.Header;
                DescriptionField.Text = property.Description;
                UrlField.Text = property.Url;
            }
        }

        // Ability to pass parameters to the editor, not used in our case
        public override string Parameters {
            set { throw new System.NotImplementedException(); }
        }
    }
}