How to Create a Custom Content Area Validator in Episerver?

In some projects, you may want to add some form of custom validation in your content areas, for example you may only want to allow 5 blocks in a carousel, or, you only want certain blocks to be allowed.  In Episerver there isn't a very easy way to do it via the UI, if you even tried it would probably involve dojo.  Instead, you can create a custom validation attribute you can add to a content area to apply your rules.  If the validation fails, the page won't save and an error message can be displayed back to the user.

using System;
using EPiServer.Core;

namespace Validator
   [AttributeUsage(AttributeTargets.Property |
                            AttributeTargets.Field |
        public class MaxItemsAllowed : ValidationAttribute
            private readonly int maxCapacity;

            public MaxItemsAllowed(int maxCapacity)
                this.maxCapacity = maxCapacity;

            public override bool IsValid(object value)
                var contentArea = value as ContentArea;
                var isValidCount = contentArea == null
                                   || contentArea.Items == null
                                   || contentArea.Count <= maxCapacity;

            return isValidCount;

            protected override ValidationResult IsValid(object value, ValidationContext validationContext)
                var result = base.IsValid(value, validationContext);

                if (!string.IsNullOrWhiteSpace(result?.ErrorMessage))
                    result.ErrorMessage = $"Content area has more than bloks than the allowed {maxCapacity}");

                return result;

To create a validation rule, all you need to do is to create a new class inheriting from ValidationAttributeand override the 2 IsValid methods. [csharp 1="Name" 2="=" 3="3=""A"" 4="Carousel" 5="5="Slide","" 6="Description" 7="=" 8="8=""A"" 9="slide" 10="for" 11="a" 12="12="carousel","" groupname="SystemTabNames.Content," order="10)" language="[Display("] [AllowedTypes(typeof(CarouselSlide))] [MaxItemsAllowed(5)] public virtual ContentArea SlideItems{ get; set; } The MaxItemsAllowed attribute can then be used to decorate the content area you care about. This method is probably the way I would recommend going.

Validating using IValidate

As of Episerver 7 there is also another way of validating a content area, this way using the IValidate interface.  Create a new class and inherit from IValidate.  On application start, EPiServer scans the solution for class that implement EPiServer.Validation.IValidate and executes the Validate() method.  This works the same way as the Page Type builder model, if a class is decorated as a ContentType, it gets picked up. The IValidate method will be called any time a page or block is saved. In your validate method, you simply specify the page type you want to filter by and then put the code inside it.

public class ContentPageValidator : IValidate<ContentPage>
  public IEnumerable<ValidationError> Validate(ContentPage page)
     if (page.ContentArea != null && page.ContentArea.Count <= maxCapacity)
        return new ValidationError[]
                    new ValidationError()
                         ErrorMessage = "Too many blocks have been added",
                         PropertyName = page.GetPropertyName<StandardPage>(p => p.ContentArea),
                         Severity = ValidationErrorSeverity.Error,
                         ValidationType = ValidationErrorType.StorageValidation

       return Enumerable.Empty<ValidationError>();

That should do you fine.  I prefer putting the attribute on the ContentArea class approach as it gives you less code duplication, but both ways work well.

Jon D Jones

Software Architect, Programmer and Technologist Jon Jones is founder and CEO of London-based tech firm Digital Prompt. He has been working in the field for nearly a decade, specializing in new technologies and technical solution research in the web business. A passionate blogger by heart , speaker & consultant from England.. always on the hunt for the next challenge

Back to top
var _gaq = _gaq || []; _gaq.push(['_setAccount', 'UA-35662136-1']); _gaq.push(['_trackPageview']); (function() { var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.src = ('https:' == document.location.protocol ? 'https://' : 'http://') + ''; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); })();