Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Bound Model Properties to a DevExpress GridView

4.73/5 (5 votes)
23 Jan 2014CPOL2 min read 33.7K  
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)