Click here to Skip to main content
15,884,836 members
Articles / Programming Languages / C#
Tip/Trick

Bound Model Properties to a DevExpress GridView

Rate me:
Please Sign up or sign in to vote.
4.73/5 (5 votes)
23 Jan 2014CPOL2 min read 33K   3   7
Bound model properties to a DevExpress GridView

Introduction

Tired of setting your DevExpress grid definition by adding columns with property name?

And what about setting column title from resources?

I built a GridViewSetting generic (typed) that lets you bound a column to a model like that:

HTML
@model IEnumerable<Model>
@{
 Html.DevExpress()
   .GridView<Model>(settings =>
   {
      settings.Name = "gridView";
      settings
         .GetColumns()
         .Bound(model => model.Field1)
         .Bound(model => model.Field2)
         .Bound(model => model.Field3)
         .Bound(model => model.Amount, column => column
                                                       .PropertiesEdit
                                                       .DisplayFormatString = "N")
         .Bound(model => model.Ratio, column => column
                                                      .PropertiesEdit
                                                      .DisplayFormatString = "P")
         .Bound(model => model.Date, column => column
                                                     .PropertiesEdit
                                                     .DisplayFormatString = "yyyy-MM-dd");
   })
   .Bind(Model)
   .GetHtml();
}

Using the Code

Let’s start with this context.

A model:

C#
public class Person
{
    public int Id { get; set; }
    [Display(Description = "First Name" )]
    public string FirstName { get; set; }
 
    [Display(Description = "Last Name" )]
    public string LastName { get; set; }
 
    [Display(Description = "Function" )]
    public string Function { get; set; }
 
    [Display(Description = "Hiring Date" )]
    public DateTime HiringDate { get; set; }
}

A controller action:

C#
public ActionResult Index()
{
    var persons = new Collection<Person>
                  {
                      new Person
                      {
                          Id = 1,
                          FirstName = "Bill",
                          LastName = "Telnor",
                          Function = "Sales Rep",
                          HiringDate = new DateTime(2011, 5, 13)
                      },
                      new Person
                      {
                          Id = 2,
                          FirstName = "Joe",
                          LastName = "Bean",
                          Function = "Home Officer",
                          HiringDate = new DateTime(1999, 12, 2)
                      },
                      new Person
                      {
                          Id = 3,
                          FirstName = "Stan",
                          LastName = "Greenspan",
                          Function = "Horticulturist",
                          HiringDate = new DateTime(2008, 3, 28)
                      },
                  };
    return View(persons);
}

And a view:

HTML
@using System.Web.UI.WebControls
@using GridViewExtensionWebSite.Models
@model IEnumerable<Person>

@{
    Html.DevExpress()
        .GridView(settings =>
                  {
                      settings .Name = "gridPerson";
                      settings .Width = Unit.Percentage(100);
                      settings .Height = Unit.Percentage(100);
                      settings 
                          .Columns
                          .Add("FirstName", "First Name");
                      settings 
                          .Columns
                          .Add("LastName", "Last Name");
                      settings 
                          .Columns
                          .Add("Function", "Function");
                      settings 
                          .Columns
                          .Add("HiringDate", "Hiring Date", 
                               MVCxGridViewColumnType.DateEdit);
                  })
        .Bind(Model)
        .GetHtml();
}

First, the original Html.DevExpress().GridView method receives a parameter Action<DevExpress.Web.Mvc.GridViewSettings>. Then we must inherit from this class to make it generic. I put all classes in the namespace DevExpress.Web.Mvc to make it simpler.

C#
namespace DevExpress.Web.Mvc
{
    public class GridViewSettings<TModel> : GridViewSettings
    {
        #region Methods

         //We will add methods here later.  Let's just see what happens now.
        #endregion
    }
}

Now we can add an extension method to the DevExpress.Web.Mvc.UI.ExtensionsFactory class. The generic GridView<TModel> method will be added but we first need to receive the view context to initialize the GridViewExtension. DevExpress has internal methods to set and get the current view context. We can make a little bit of code reflection or create our own ExtensionsFactory, but why not just keep it simple and pass the view context in parameter to the GridView method.

Now, let’s reproduce basically the same behavior than DevExpress by instantiating our own typed GridViewSettings<TModel>. Pass it to the delegate and return a new GridViewExtension.

C#
using System;
using DevExpress.Web.Mvc.UI;
namespace DevExpress.Web.Mvc
{
    public static class GridViewExtensions
    {
        public static GridViewExtension GridView<TModel>(
                      this ExtensionsFactory extensionsFactory,
                      ViewContext viewContext,
                      Action<GridViewSettings<TModel>> action)
        {
            var gridViewSettings = new GridViewSettings<TModel>();
            action(gridViewSettings);
            return new GridViewExtension(gridViewSettings, viewContext);
        }
    }
}

Is that all? Not yet. We will modify our view…

HTML
@{
    Html.DevExpress()
        .GridView<Person>(Html.ViewContext, gridViewSettings =>
                  {
                      …
                  })
        .Bind(Model)
        .GetHtml();
}

If we run the cursor (in Visual Studio) over the GridViewSettings variable, we see that its type is now GridViewSettings<Person>. That's good, but what does it give us? Nothing!

We need to replace the usage of the GridViewSettings.Column property by something more efficient. GridViewSettings.Column returns a MVCxGridViewColumnCollection, our GridViewSettings<TModel> will return a MVCxGridViewColumnCollection<TModel>.

Which method do we need to manage the columns? Our needs are pretty simple; we need to bound a column to a property of the model, one to bound with a property and edit column properties, and another one to add a calculated, static, action or other type of column.

And by the way, why not make it fluent...

C#
using System;
using System.Linq.Expressions;
using System.Web.Mvc;
using DevExpress.Utils;

namespace DevExpress.Web.Mvc
{
    public class MVCxGridViewColumnCollection<TModel>
    {
        #region Ctors

        public MVCxGridViewColumnCollection(MVCxGridViewColumnCollection columns)
        {
            ColumnCollection = columns;
        }

        #endregion

        #region Properties

        public MVCxGridViewColumnCollection ColumnCollection { get; private set; }

        #endregion

        #region Methods

        public MVCxGridViewColumnCollection<TModel> Bound<TValue>(
                                            Expression<Func<TModel, TValue>> expression, 
                                            Action<MVCxGridViewColumn> setColumn)
        {
            if (expression == null)
                throw new ArgumentNullException("expression");

            setColumn(GetNewColumn(expression));
            return this;
        }

        public MVCxGridViewColumnCollection<TModel> Bound<TValue>(
                                            Expression<Func<TModel, TValue>> expression)
        {
            if (expression == null)
                throw new ArgumentNullException("expression");

            GetNewColumn(expression);

            return this;
        }

        public MVCxGridViewColumnCollection<TModel> Add(Action<MVCxGridViewColumn> setColumn)
        {
            //Add a basic MVCxGridViewColumn.
            var gridViewColumn = ColumnCollection.Add();
            setColumn(gridViewColumn);
            return this;
        }

        private MVCxGridViewColumn GetNewColumn<TValue>(
                                            Expression<Func<TModel, TValue>> expression)
        {
            //Add a basic MVCxGridViewColumn reproduce sensibly what MVC does 
            //in DisplayFor<TMember> method.
            var metaData = ModelMetadata.FromLambdaExpression(expression, 
                                                           new ViewDataDictionary<TModel>());
            var gridViewColumn = ColumnCollection.Add();
            gridViewColumn.Caption = metaData.DisplayName;
            gridViewColumn.FieldName = metaData.PropertyName;
            gridViewColumn.HeaderStyle.Wrap = DefaultBoolean.True;
            return gridViewColumn;
        }

        #endregion
    }
}

Now, let's change our GridViewSettings<TModel> and add a method to get the column collection.

C#
namespace DevExpress.Web.Mvc
{
    public class GridViewSettings<TModel> : GridViewSettings
    {
        #region Methods

        public MVCxGridViewColumnCollection<TModel> GetColumns()
        {
            return new MVCxGridViewColumnCollection<TModel>(Columns);
        }

        #endregion
    }
}

We'll now be able to replace the usage of the Columns property of the GridViewSettings in the view by our new method GetColumns.

HTML
@model IEnumerable<Person>
@{
 Html.DevExpress()
     .GridView<Person>(Html.ViewContext , settings =>
     {
        settings.Name = "gridPerson";
        settings.Width = Unit.Percentage(100);
        settings.Height = Unit.Percentage(100);
        settings.GetColumns()
                .Bound(model => model.FirstName)
                .Bound(model => model.LastName)
                .Bound(model => model.Function)
                .Bound(model => model.HiringDate, 
                                column => column.PropertiesEdit
                                                .DisplayFormatString = "yyyy-MM-dd");
     })
     .Bind(Model)
     .GetHtml();
}

Done deal!

In the source code, I also add code to bound the model to a band column (multi row headers).

I hope you will understand that I don't use bootstraper, view-model, repository and I don't know what other best practices to create this demo. I want to show you a DevExpress extension, not a full solution!

Thanks.

History

  • Created on 01-23-2013

License

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


Written By
Architect CGI
Canada Canada
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionVery good article! Pin
Volynsky Alex24-Jan-14 3:40
professionalVolynsky Alex24-Jan-14 3:40 
AnswerRe: Very good article! Pin
Yves Vaillancourt24-Jan-14 3:54
Yves Vaillancourt24-Jan-14 3:54 
GeneralRe: Very good article! Pin
Volynsky Alex24-Jan-14 4:48
professionalVolynsky Alex24-Jan-14 4:48 
GeneralRe: Very good article! Pin
Yves Vaillancourt24-Jan-14 5:34
Yves Vaillancourt24-Jan-14 5:34 
GeneralRe: Very good article! Pin
Volynsky Alex24-Jan-14 6:24
professionalVolynsky Alex24-Jan-14 6:24 
GeneralRe: Very good article! Pin
Yves Vaillancourt24-Jan-14 7:11
Yves Vaillancourt24-Jan-14 7:11 
GeneralRe: Very good article! Pin
Volynsky Alex24-Jan-14 7:54
professionalVolynsky Alex24-Jan-14 7:54 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.