Click here to Skip to main content
15,886,830 members
Articles / MVC

Kendo Grid MVC Wrapper Automatic Column Configuration

Rate me:
Please Sign up or sign in to vote.
0.00/5 (No votes)
20 Nov 2015CPOL5 min read 8.8K   1  
Kendo Grid MVC Wrapper Automatic Column Configuration

Originally posted on http://geekswithblogs.net/sdorman/archive/2015/11/05/kendo-grid-mvc-wrapper-automatic-column-configuration.aspx.

The Telerik Kendo Grid control is really powerful, especially when combined with the MVC wrappers. One of the things that make the MVC wrapper so useful is the ability to automatically (and easily) generate data-bound columns. It’s a single line of code:

C#
.Columns(columns => columns.AutoGenerate(true))

The code behind AutoGenerate(true) understands some of the <a href="https://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations(v=vs.110).aspx">System.ComponentModel.DataAnnotations</a> attributes. Specifically, it knows how to automatically configure the grid column for these attributes:

AttributeDescription
<a href="https://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.compareattribute(v=vs.110).aspx">Compare</a>Compares two properties
<a href="https://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.creditcardattribute(v=vs.110).aspx">CreditCard</a>Specifies that a data field value is a credit card number
<a href="https://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.customvalidationattribute(v=vs.110).aspx">CustomValidation</a>Specifies a custom validation method that is used to validate a property
<a href="https://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.datatypeattribute(v=vs.110).aspx">DataType</a>Specifies the name of an additional type to associate with a data field
<a href="https://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.displayattribute(v=vs.110).aspx">Display</a>Provides a general-purpose attribute that lets you specify localizable strings for types and members of entity partial classes
<a href="https://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.displaycolumnattribute(v=vs.110).aspx">DisplayColumn</a>Specifies the column that is displayed in the referred table as a foreign-key column
<a href="https://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.displayformatattribute(v=vs.110).aspx">DisplayFormat</a>Specifies how data fields are displayed and formatted by ASP.NET Dynamic Data
<a href="https://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.editableattribute(v=vs.110).aspx">Editable</a>Indicates whether a data field is editable
<a href="https://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.emailaddressattribute(v=vs.110).aspx">EmailAddress</a>Validates an email address
<a href="https://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.enumdatatypeattribute(v=vs.110).aspx">EnumDataType</a>Enables a .NET Framework enumeration to be mapped to a data column
<a href="https://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.fileextensionsattribute(v=vs.110).aspx">FileExtensions</a>Validates file name extensions
<a href="https://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.filteruihintattribute(v=vs.110).aspx">FilterUIHint</a>Represents an attribute that is used to specify the filtering behavior for a column
<a href="https://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.maxlengthattribute(v=vs.110).aspx">MaxLength</a>Specifies the maximum length of array or string data allowed in a property
<a href="https://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.minlengthattribute(v=vs.110).aspx">MinLength</a>Specifies the minimum length of array or string data allowed in a property
<a href="https://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.phoneattribute(v=vs.110).aspx">Phone</a>Specifies that a data field value is a well-formed phone number using a regular expression for phone numbers
<a href="https://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.rangeattribute(v=vs.110).aspx">Range</a>Specifies the numeric range constraints for the value of a data field
<a href="https://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.regularexpressionattribute(v=vs.110).aspx">RegularExpression</a>Specifies that a data field value in ASP.NET Dynamic Data must match the specified regular expression
<a href="https://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.requiredattribute(v=vs.110).aspx">Required</a>Specifies that a data field value is required
<a href="https://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.scaffoldcolumnattribute(v=vs.110).aspx">ScaffoldColumn</a>Specifies whether a class or data column uses scaffolding
<a href="https://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.stringlengthattribute(v=vs.110).aspx">StringLength</a>Specifies the minimum and maximum length of characters that are allowed in a data field
<a href="https://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.uihintattribute(v=vs.110).aspx">UIHint</a>Specifies the template or user control that Dynamic Data uses to display a data field
<a href="https://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.urlattribute(v=vs.110).aspx">Url</a>Provides URL validation

What’s nice about this support is that you can use these attributes to adorn your model properties and declaratively describe some of the metadata about the column.

The problem, though, is that you can’t also set the Kendo Grid specific properties, such as column width, if the column is locked or not, and if it should be included in the column menu (to name just a few).

Fortunately, we can hook into the <a href="https://msdn.microsoft.com/en-us/library/system.web.mvc.modelmetadata.additionalvalues(v=vs.118).aspx#P:System.Web.Mvc.ModelMetadata.AdditionalValues">AdditionalValues</a> dictionary of the Metadata property on a data-bound column (which is of type Kendo.Mvc.UI.GridBoundColumn<TModel, TValue>). This property holds an instance of a <a href="https://msdn.microsoft.com/en-us/library/system.web.mvc.modelmetadata(v=vs.110).aspx">System.Web.Mvc.ModelMetadata</a> (more specifically an instance of a <a href="https://msdn.microsoft.com/en-us/library/gg512055(v=vs.118).aspx">CachedModelMetadata<TPrototypeCache></a>) object, which has all of the metadata related attributes defined on the properties of the model and is the key to our solution of providing automatic column configuration based on data annotation attributes. To do this, we simply define our own attribute and implement the <a href="https://msdn.microsoft.com/en-us/library/system.web.mvc.imetadataaware(v=vs.118).aspx">IMetadataAware</a> interface. The runtime will handle everything for us and our new attribute will be added to the AdditionalValues dictionary.

I created a GridColumnAttribute to allow all of the additional Kendo specific properties to be set.

C#
using System;
using System.Web.Mvc;

namespace Cadru.Web.KendoExtensions
{
    public class GridColumnAttribute : Attribute, IMetadataAware
    {
        public const string Key = "GridColumnMetadata";

        public bool Hidden { get; set; }

        public bool IncludeInMenu { get; set; }

        public bool Lockable { get; set; }

        public bool Locked { get; set; }

        public int MinScreenWidth { get; set; }

        public string Width { get; set; }

        public void OnMetadataCreated(ModelMetadata metadata)
        {
            metadata.AdditionalValues[GridColumnAttribute.Key] = this;
        }
    }
}

Now, we can decorate our model with the new attribute:

C#
public class EmployeeModel
{
    [Editable(false)]
    [GridColumn(Width = "100px", Locked = true)]
    public string EmployeeID { get; set; }

    [GridColumn(Width = "200px", Locked = true)]
    public string EmployeeName { get; set; }

    [GridColumn(Width = "100px")]
    public string EmployeeFirstName { get; set; }

    [GridColumn(Width = "100px")]
    public string EmployeeLastName { get; set; }
}

However, that’s only part of the solution. We still need to tell the Kendo Grid that it needs to do something with this new attribute. To do this, we can use the overload for the AutoGenerate method which takes an Action:

C#
.Columns(columns => columns.AutoGenerate(c => GridColumnHelpers.ConfigureColumn(c)))

The ConfigureColumns method looks like:

C#
using Kendo.Mvc.UI;
using System;
using System.Web.Mvc;

namespace Cadru.Web.KendoExtensions
{
    public static class GridColumnHelpers
    {
        public static void ConfigureColumn<T>(GridColumnBase<T> column) where T : class
        {
            CachedDataAnnotationsModelMetadata metadata = ((dynamic)column).Metadata;
            object attributeValue = null;
            if (metadata.AdditionalValues.TryGetValue(GridColumnAttribute.Key, out attributeValue))
            {
                var attribute = (GridColumnAttribute)attributeValue;
                column.Width = attribute.Width;
                column.Locked = attribute.Locked;
                column.Hidden = attribute.Hidden;
                column.IncludeInMenu = attribute.IncludeInMenu;
                column.Lockable = attribute.Lockable;
                column.MinScreenWidth = attribute.MinScreenWidth;
            }
        }
    }
}

This takes advantage of the fact that the method is being called in the context of automatically generating data-bound columns, so it’s able to take the column and cast it to a dynamic object in order to reference the Metadata property. We have to do this because the IGridBoundColumn doesn’t expose the Metadata property and we can’t cast it directly to a GridBoundColumn<TModel, TValue> because (among other reasons) we don’t know the type for TValue. That leaves us with casting it to dynamic and letting the dynamic dispatcher figure out how to give us back the requested property. From there, we look to see if the AdditionalValues dictionary contains our attribute, and if it does we then set the column properties to their respective values as specified by the attribute. We now have the ability to automatically configure the additional column properties using metadata specified in our model while still automatically generating the data-bound columns.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
I am a Microsoft C# MVP, author, speaker, blogger, and software developer. I also created the WP Requests and WinStore Requests sites for Windows Phone and Windows Sotre apps as well as several open source projects.

I've been involved with computers in one way or another for as long as I can remember, but started professionally in 1993. Although my primary focus right now is commercial software applications, I prefer building infrastructure components, reusable shared libraries and helping companies define, develop and automate process and code standards and guidelines.

Comments and Discussions

 
-- There are no messages in this forum --