HomeDev GuideRecipesAPI Reference
Dev GuideAPI ReferenceUser GuideGitHubNuGetDev CommunityOptimizely AcademySubmit a ticketLog In
Dev Guide

Develop properties

Describes how to access and use properties in Optimizely Content Management System (CMS).

Properties store and present data for content such as pages and blocks. Defining a property on a content type stores it as a PropertyDefinition class. Runtime instances of the property type inherit from PropertyData. These classes manage the data inside the property: protecting read-only state, preparing data for saving and loading, and declaring the value type. PropertyData classes also provide a default user interface. See also Configure properties.

Access a property on a read-only page

Access property values from content items that CMS returns in read-only mode from the API. The following code accesses a property on the StandardPage type of PageData:

public class StandardPageController: PageControllerBase<StandardPage> {
  public ActionResult Index(StandardPage currentPage) {
    Object propertyValue = currentPage["PropertyName"];
    // do something else and return view
  }
}

The accessor checks whether PropertyName exists on the page by inspecting the Property collection defined on IContentData, implemented by PageData (which inherits PageController). It returns the property value if one exists. If the property is not on the page, a delegate checks whether it exists elsewhere. The default delegate looks for the property from the fetch data from function (if activated on the current page). The delegate also checks dynamic properties. Write your own delegate to change this behavior.

Access a property on a write-enabled page

Understand how property access differs when a page is in write mode. If the page is write-enabled, the Property accessor returns only properties defined on the page. Dynamic properties and fetch-data-from properties are not returned in write mode.

Access properties on strongly typed content

Strongly typed content lets you work with pages and blocks as ordinary .NET objects. Given a strongly typed page with the following signature:

[ContentType]
public class StandardPage: SitePageData {
  public virtual XhtmlString MainBody {
    get;
    set;
  }
}
📘

Note

Why is the property declared as virtual? In the background, a proxy class is created for the page type, where the typed property on your class is connected to the backing PropertyData instance. This only works if properties are declared as virtual otherwise they cant be overridden. If the properties are not declared virtual, you need to implement get/set so they read/write data to the underlying property collection instead.

If you use the generic EPiServer.Web.Mvc in your page inheritance, access the MainBody property like this:

public class StandardPageController: PageController<StandardPage> {
  public ActionResult Index(StandardPage currentPage) {
    var mainBody = currentPage.MainBody;
    // do something else and return view
  }
}

Extension methods for strongly typed content

The GetPropertyValue and SetPropertyValue extension methods take advantage of strongly typed models and override the get and set behavior for the property. Add a using statement referencing the EPiServer.Core namespace to your class to access these extension methods.

The following example shows a strongly typed content model that has a fallback behavior for its PageHeading property. If the editor sets the PageHeading page property, the PageHeading property on the model returns the set value. If the editor does not set it, the property on the model returns the PageName property instead:

[ContentType]
    public class StandardPage: SitePageData
    {
      public virtual string PageHeading
      {
        get
        {
          var heading = this.GetPropertyValue(p => p.PageHeading);
     
          // If heading is not set, fall back to PageName
          return String.IsNullOrWhiteSpace(heading) ? PageName : heading;
        }
        set { this.SetPropertyValue(p => p.PageHeading, value); }
      }
    }

Access properties on non-strongly typed content

Access property values directly through their object representation when the property type is unknown or when iterating over multiple properties.

public ActionResult Index(StandardPage currentPage) {
  object propertyValue = currentPage["PropertyName"];
  // do something else and return view
}

If you know the type of your value, cast it directly.

public ActionResult Index(StandardPage currentPage) {
  string propertyValueString = (string) currentPage["PropertyName"];
  // do something else and return view
}

If you are unsure of the actual type and want to avoid an exception, assign the property value with the as keyword:

public ActionResult Index(StandardPage currentPage) {
  string propertyValueAsString = currentPage["PropertyName"] as string;
  if (!String.IsNullOrEmpty(propertyValueAsString)) {
    DoSomething();
  }
  // do something else and return view
}

Do not assume the value type for a property type. For example, the values of PropertyXhtmlString are of type XhtmlString, not string. So, casting the value of a PropertyXhtmlString to string causes an exception, and using the as string operation yields null.

Use GetPropertyValue for non-strongly typed content

The GetPropertyValue extension method simplifies property access and reduces code. Import the EPiServer.Core namespace (for example, with a using statement in code behind) to extend ContentData with GetPropertyValue. Use it to get the string representation of a property without checking against null, or to provide a default value when the property is null.

The following usage examples show different overloads of GetPropertyValue:

public ActionResult Index(StandardPage currentPage) {
  // 1. Get as string, defaults to null and otherwise calls ToString for the value
  string mainBody = currentPage.GetPropertyValue("MainBody");

  // 2. Specify a fallback value
  string mainBodyFallback = currentPage.GetPropertyValue("MainBody", string.Empty);

  // 3. Which is equivalent to
  string mainBodyFallback2 = currentPage.GetPropertyValue("MainBody") ?? string.Empty;

  // 4. So a common usage is probably
  string pageHeading = currentPage.GetPropertyValue("PageHeading", currentPage.PageName);

  // 5. Get typed value, defaults to null if property is not set or defined
  XhtmlString xhtml = currentPage.GetPropertyValue<XhtmlString>("MainBody");

  // 6. Which supports fallback in the same way as the "ToString" overload
  XhtmlString xhtmlWithFallback =
    currentPage.GetPropertyValue<XhtmlString>("MainBody", new XhtmlString());

  // 7. Advanced: Specify a conversion for the value
  // Note: this example is very similar to 1. since we just call ToString, EXCEPT
  // if MainBody is something else than an Xhtml property in which case 1. would still
  // return the ToString representation while this would throw an exception since
  // it tries to cast the value to an XhtmlString before handing it over to the
  // conversion lambda
  string convertedXhtml =
    currentPage.GetPropertyValue<XhtmlString,string>("MainBody", x => x.ToString());

  // 8. This is equivalent to 1. since it treats the property value as object
  string mainBody2 =
    currentPage.GetPropertyValue<object, string>("MainBody", x => x.ToString());

  // do something else and return view
}

Use a property multiple times

Save a reference to a property used multiple times to avoid repeated lookups. Otherwise, each call fetches the property from the internal collection, which may also trigger unnecessary delegate calls if the property is not native to the page.

For example, use:

public ActionResult Index(StandardPage currentPage) {
  PropertyData property = currentPage.Property["PropertyName"];
  if (property != null && !property.IsNull) {
    DoSomething();
  }
  // do something else and return view
}

Instead of:

public ActionResult Index(StandardPage currentPage) {
  if (currentPage.Property["PropertyName"] != null && !currentPage.Property["PropertyName"].IsNull) {
    DoSomething();
  }
  // do something else and return view
}

Check null values in properties

Handle null property values correctly to avoid runtime exceptions. CMS does not store properties with an empty value in the database. If you access a property from code, it is null: not an empty string, 0, or false. This makes null a convenient way to check if an editor has not set a value or if the property does not exist on the page. Compare with null regardless of the data type.

The following example throws a NullReferenceException if the value is empty or missing:

public ActionResult Index(StandardPage currentPage) {
  StringBuilder sb = new StringBuilder();
  sb.Append(currentPage["PropertyName"].ToString());
  // do something else and return view
}

StringBuilder.Append accepts null objects, so this example is better than the previous example:

public ActionResult Index(StandardPage currentPage) {
  StringBuilder sb = new StringBuilder();
  sb.Append(currentPage["PropertyName"]);
  // do something else and return view
}

The following example throws a NullReferenceException if the value is empty or missing:

<%= @ViewModel.CurrentPage.Property["PropertyName"].Value.ToString()%>

Markup accepts any type and converts to string, so casting is not needed:

<%= @ViewModel.CurrentPage["PropertyName"] %>

The following example shows the fallback for src attributes:

<img src='<%= @ViewModel.CurrentPage["ImageUrl"] ?? "/Missing.png" %>' />

To return a string, use the ?? operator as follows:

string s = currentPage["StringProperty"] as string ?? string.Empty;

The following example returns a value type:

DateTime date = (DateTime)(currentPage["DateProperty"] ?? DateTime.Now);
int i = (int)(currentPage["IntegerProperty"] ?? 0);

Another fallback in the markup using the ?? operator as follows:

<%= currentPage["Heading"] ?? currentPage.PageName %>

Update values for a page

Create a writable clone of a page before modifying its property values. Ensure you have a writable version of the page, as shown in the following example.

public ActionResult Index(StandardPage currentPage) {
  PageData writablePage = currentPage.CreateWritableClone();
  // do something else and return view
}

To set values, assign them directly to the writablePage object by working against the properties for the most common page properties or by accessing the values directly:

writablePage.SomeProperty = "somevalue";

This is the same as:

writablePage["SomeProperty"] = "somevalue";

When you are done with the page, save it by using IContentRepository (available in DI container):

_contentRepository.Save(writablePage, EPiServer.DataAccess.SaveAction.Publish);

📘

Note

You cannot save dynamic properties or properties that reside on other pages when you are working against IContentRepository.Save.

HTML encoding and Optimizely CMS page properties

Encode special characters in page properties to ensure valid XHTML output. HTML has special handling for characters such as < > and symbols ? & /. Some browsers truncate or corrupt spaces, and problems arise when displaying these characters. For XHTML-valid output, use the HttpUtility.HtmlEncode method on strings injected with inline expressions. HttpUtility.HtmlEncode is part of .NET Core.

When outputting strings directly in Razor views, encoding is usually unnecessary. Razor encodes strings inside a code block by default. The following example displays the CMS Name property:

<h2>@ViewModel.CurrentPage.Name</h2>