How to dynamically bind and configure column’s metadata in kendo grid for your MVC application

As a blogger you always strive for some interesting topic to write for your blog. Recently in one of our MVC application i got the chance to work with kendo grid control, but with some interesting requirement. Although i have already used kendo ui controls in other mvc application, but in this application i have had got very interesting requirement and somewhat challenging also.

Now let me explain you the requirement. Normally you will bind your kendo grid with some predefined columns. But in this case i need to bind the kendo grid where column type and number of columns to bind will decide at run-time. Let me add further one more point in this requirement – each column’s metadata will also decide at run-time. Well, column’s metadata mean column’s caption, datatype, width, format, header alignment and text alignment etc. will decide based on the type of column. Don’t worry, if you don’t get this point. Let’s see this practically.

For demo purpose, i have made two new classes. One class for actual data to bind in kendo grid. Name of that class is let say – RowValue. Another class is for column’s metadata. Name of that class is – ColumnMetaDataInfo
Here is the code of our both the classes.


public class RowValue
{
	public string PatientName { get; set; }

	public decimal OrderAmount { get; set; }

	public int PatientAge { get; set; }

	public DateTime OrderDate { get; set; }
}

public class ColumnMetaDataInfo
{
	public string FieldName { get; set; }

	public int Width { get; set; }

	public string Caption { get; set; }

	public string Format { get; set; }

	public string Align { get; set; }

	public bool Display { get; set; }

	public Type DataType { get; set; }
}

Well, for demo purpose i have defined the two classes, In real scenario i have two separate tables in the database – one for actual data and another for column metadata. Now let’s use both of these classes in Model which we’ll pass it in our index.cshtml View.


public class GridViewModel
{
       public List<ColumnMetaDataInfo> Columns { get; set; }

       public List<RowValue> RowValues { get; set; }
}

Now its time to initialize the above viewModel class and fill the defined property with required data in “Home” controller’s action method.


public class HomeController : Controller
{

	public ActionResult Index()
	{
		var gridViewModel = new GridViewModel()
		{
			Columns = new List<ColumnMetaDataInfo>()
			{
				new ColumnMetaDataInfo
					{
						Caption = "Patient Name",
						FieldName = nameof(ColumnInfo.PatientName),
						Width = 50,
						Display = true,
						Format = "",
						Align = "left",
						DataType = typeof(string)
					},
				new ColumnMetaDataInfo
					{
						Caption = "Order Amount",
						FieldName = nameof(ColumnInfo.OrderAmount),
						Width = 15,
						Display = true,
						Format = "{0:C2}",
						Align = "right",
						DataType = typeof(decimal)
					},
				new ColumnMetaDataInfo
					{
						Caption = "Patient Age",
						FieldName = nameof(ColumnInfo.PatientAge),
						Width = 15,
						Display = false,
						Format = "{0:N2}",
						Align = "right",
						DataType = typeof(int)
					},
				new ColumnMetaDataInfo
					{
						Caption = "Order Date",
						FieldName = nameof(ColumnInfo.OrderDate),
						Width = 20,
						Display = true,
						Format = "{0:MM/dd/yyyy}",
						Align = "center",
						DataType = Type.GetType("System.DateTime")
					}
			},
			// Actual value to bind for each row
			RowValues = new List<ColumnInfo>()
			{
				new ColumnInfo { PatientName = "Krishnraj Rana", OrderAmount = 100.1M, PatientAge = 25, OrderDate = new DateTime(2016, 11, 25) },
				new ColumnInfo { PatientName = "Vishal Vagadia", OrderAmount = 200.1M, PatientAge = 35, OrderDate = new DateTime(2016, 11, 29) },
				new ColumnInfo { PatientName = "Birju Patel", OrderAmount = 300, PatientAge = 45, OrderDate = new DateTime(2016, 11, 28) }
			}
		};

		return View(gridViewModel);
	}
}

Now its time to bind the model with kendo grid and here is the code of Index.cshtml view in which we pass the above ViewModel class.


@model KendoGridDynamicColumn.Models.GridViewModel

@{
    ViewBag.Title = "Home Page";
}
	<link rel="stylesheet" href="http://cdn.kendostatic.com/2014.3.1316/styles/kendo.common.min.css" />
	<link rel="stylesheet" href="http://cdn.kendostatic.com/2014.3.1316/styles/kendo.default.min.css" />
<script src="~/Scripts/jquery-1.12.3.min.js"></script>
<script src="~/Scripts/kendo.all.min.js"></script>
<script src="~/Scripts/kendo.aspnetmvc.min.js"></script>


<div class="jumbotron">

<h2>Dynamic Kendo Grid</h2>


<h4>POC - V.1.0.0</h4>

</div>



<div class="row">

<div class="kendolist">
        @(Html.Kendo().Grid(Model.RowValues)
            .Name("grdDynamicCol")
            .Columns(columns =>
            {
                foreach (var column in Model.Columns)
                {
                    columns.Bound(column.FieldName).Title(column.Caption)
                        //.Visible(column.Display)
                        .Format(column.Format)
                        .Width($"{column.Width}%")
                        .HtmlAttributes(new { @style = $"text-align:{column.Align.ToLower()}" })
                        .HeaderHtmlAttributes(new { @style = $"text-align:{column.Align.ToLower()}" });
                }
            })
            .Pageable()
            .Sortable()
            .Resizable(resize => resize.Columns(true))
            .DataSource(dataSource => dataSource
                .Ajax()
                .Model(model =>
                {
                    foreach (var column in Model.Columns)
                    {
                        model.Field(column.FieldName, column.DataType);
                    }
                })
            )
        //.ColumnMenu()
        //.HtmlAttributes(new { @class = "gridview" })
        )
</div>

</div>

<script>
    $(document).ready(function () {

        HideGridColumn();
    });

    function HideGridColumn() {
        var grid = $("#grdDynamicCol").data("kendoGrid");
        @foreach (var column in Model.Columns.Where(col => col.Display == false))
        {
            <text>grid.hideColumn('@column.FieldName');</text>
        }
    }

    function gvchange(e) {
        var grid = $("#grdDynamicCol").data("kendoGrid");
        if (e.checked) {
            grid.showColumn(e.id);
        } else {
            grid.hideColumn(e.id);
        }
    }
</script>

and when grid binds, this is how it look in the browser.

result

That’s it. For any query or doubt, write in the comment section. Hope you enjoyed this post.

Advertisements

Get list of property value from another list based on supplied property name using indexer in c#

Recently in one of our MVC application, I have had a requirement to read property value from one generic list by using property name, i.e. For exa. Consider one function named – GetPropertyValueList() which has one parameter named – propertyName. By using that parameter this function return the List<object> (List of property value).

OK, Let me explain further my requirement in detail. One of our end user wanted to migrate some of his customers manually. So I had to develop one page which displays specific customer list retrieve from the database. Now this page contains different sections to display list of property value. For exa. “LastName” property section displays only list of customer’s last name in radio-button list. Same way in other section let say “Age” property section displays only list of customer’s age in radio-button list. Same way for other properties too of customer class.

Now let’s see this practically. To achieve this functionality I need to develop several modal classes in my application’s modal layer. For demo purpose I have shown you these things in action in one console application in this post.

My first modal class is a normal “Customer” class.

public class Customer
{

    public string FirstName { get; set; }

    public string LastName { get; set; }

    public int Age { get; set; }

    public string MobileNo { get; set; }

    // Same way there are more than 20 properties in my mvc application
    //public string Address { get; set; }
}

My second modal class is “SourceCustomerInfo” class.

public class SourceCustomerInfo
{
	///<summary>
	/// To get the name of property at run time of Customer class
	/// </summary>

	public Customer customerField { get; set; }

	///<summary>
	/// Contain list of customers from database to migrate
	/// </summary>

	public List<Customer> customerList { get; set; }

}

 

In SourceCustomerInfo class first property customerField is used to get list of property value from customerList property. You can see the detail usage of this class in our console application.

My third modal class is “CustomerFieldValueList” class.

public class CustomerFieldValueList
{
	///<summary>
	/// Contain name of the property to render in the lable
	/// </summary>

	public string FieldName { get; set; }

	///<summary>
	/// Contain list of property value to render in the radio-button list
	/// </summary>

	public List<object> FieldValueList { get; set; }

}

Why I need to develop this modal ? Well, my MVC View renders all labels and its related radio-button list dynamically (By using above modal class). In the above class first property FieldName is used as label in MVC View and second property FieldValueList is used to fill radio-button list. Hmmm sorry for my bad English, But Here is the screen-shot of this class of what you’ll get at run time in each property for better understanding.

FieldValueList

All right, now the key point is here. To achieve my key requirement I need to use “Indexers” – a very special type of class member in C#. An indexer allows an object to be indexed like an array. The main use of “indexers” is to support the creation of specialized arrays or list. For more information on “Indexers” refer this MSDN link.

Here is the indexer that I have added in “Customer” class.

public class Customer
{

	///<summary>
	/// Get the value by supplied property name
	/// </summary>
	/// <param name="propertyName"></param>
	/// <returns></returns>
	public object this[string propertyName]
	{
		get
		{
			return this.GetType().GetProperty(propertyName).GetValue(this, null);
		}
	}

	public string FirstName { get; set; }

	public string LastName { get; set; }

	public int Age { get; set; }

	public string MobileNo { get; set; }

	// Same way there are more than 20 properties in my mvc application
	//public string Address { get; set; }

}

As you can see that I have read property value by using propertyName as parameter thru reflection in indexer.
Let’s combine all of these modal classes in one console application and see it in action. Here i have show you only the Main() method code. You can download the full source code from here.

static void Main(string[] args)
{
	var sourceCustomerInfo = new SourceCustomerInfo() {customerField = new Customer() };

	// Get the name of all property of customer class using reflection in array.
	var arrayPropNames = sourceCustomerInfo.customerField
							.GetType()
							.GetProperties()
							.Select(p => p.Name)
							.ToArray();
	// Fill the Customer list
	sourceCustomerInfo.customerList = new List<Customer>
	{
		new Customer() { FirstName="Kapil", LastName="Dev", Age=55, MobileNo="123" },
		new Customer() { FirstName="Sachin", LastName="Tendulkar", Age=45, MobileNo="123" },
		new Customer() { FirstName="Rahul", LastName="Dravid", Age=50, MobileNo="123" }
	};

	var fieldValueList = new List<CustomerFieldValueList>();
	//Now we iterate thru each property name that we already get in array
	foreach (var propertyName in arrayPropNames)
	{
		// Check propertyName is not blank or not our indexers
		if (!string.IsNullOrEmpty(propertyName) && propertyName != "Item")
		{
			// Initialize new instance of CustomerFieldValueList class
			var customerFieldValueList = new CustomerFieldValueList();

			// Set customerField with current property name
			customerFieldValueList.FieldName = propertyName;

			// Get the list of property value by supplied propertyName parameter using indexers
			// from the customerList property of SourceCustomerInfo class.
			customerFieldValueList.FieldValueList = sourceCustomerInfo.customerList.Select(p => p[propertyName]).ToList();

			// Now add this class instance into fieldValueList List.
			fieldValueList.Add(customerFieldValueList);
		}
	}

	// Now we are ready to render it in our MVC View.
	// Here we'll render it in console.
	fieldValueList.ForEach(obj =>
		{
			// Display FieldName value after 15 spaces
                        Console.WriteLine("FieldName  => {0, 15}", obj.FieldName);

			obj.FieldValueList.ForEach(lst =>
				{
					// Display property value after 15 spaces
					Console.WriteLine("     List  => {0, 15}", lst);
				});
			Console.WriteLine("=========================================");
		});

	Console.ReadLine();
}

And here is the output in console.

FinalResult

That’s it folks. Hope you like this post. Leave your comments.

ASP.NET MVC: Post a list or collection of complex types with non-sequential indices

In ASP.Net MVC when you post your form then DefaultModelBinder bind your form’s submitted value. Now what if your model is list / collection of complex type which you want to post and the case is become even worse when the sequence or let say index is not maintained. Okay let’s see it in detail.

In one of my project developed in MVC 4, I had a requirement to bind list of complex type model to view. Furthermore, from this view user can add or remove items using Jquery / JavaScript before submitting it. Let’s create this situation. Let say, I want to add more than one students in “ClassRoom” table in single submit. So, I have model named – “ClassRoomViewModel” which contain property “Students” of type List.

public class ClassRoomViewModel
{
	public List<Student> Students { get; set; }
}

public class Student
{
	public string StudentName { get; set; }
	public int Age { get; set; }
}

As this is student entry form. So it opens as an empty form using below Action method that renders a view.

public ActionResult Index()
{
	var classRoom = new ClassRoomViewModel()
	{
		Students = new List<Student>()
	};
	
	return View(classRoom);
}

Now let’s create a view which call another partial view. Here is the code snippet.

@model PostListWithBrokenSequence.Models.ClassRoomViewModel

@{
	ViewBag.Title = "Home Page";
}
<div class="jumbotron">
<h1>Enroll Students</h1>
</div>
<div class="row">
	@using (Html.BeginForm())
	{
		<div class="col-md-4">
			<h2>Add Student</h2>
			@Html.Partial("_PartialAddStudent", 0)

			@Html.ActionLink("Add Student", "LoadBlankFormView", null, new {@id = "lnkAddStudent", @class = "btn btn-primary"})
			<button class="btn btn-default" type="submit"> Save </button>
		</div>
	}
</div>

And for partial view named – “_PartialAddStudent.cshtml”. here is code snippet.

@model int

<div class="form-horizontal" id="dv_@Model" data-rownum="@Model">
    <!-- <input type="hidden" name="Students.Index" value="@Model" /> -->
    <div class="form-group">
        @Html.Label("Name", new { @class = "control-label" })
        <div>
            @Html.TextBox("Students[" + Model + "].StudentName", "", new { @class = "form-control" })
        </div>
    </div>

    <div class="form-group">
        @Html.Label("Age", new {@class = "control-label"})
        <div>
            @Html.TextBox("Students[" + Model + "].Age", "", new {@class = "form-control"})
        </div>
    </div>
    <div class="form-group">
        <a href="#" class="deleteStudent btn btn-primary">Remove</a>
    </div>
    <hr/>
</div>

When you run the application it looks like this in browser.

AddStudentForm

To bind complex objects like list etc., we need to provide an index for each row as shown in “_PartialAddStudent” view. Yes, Index which start with 0 and incremented by 1. That’s why I render “_PartialAddStudent” view with 0 index in “Index” view using HTML.Partial() method. As you see in the above code that I have manually build names and ids of all control in the partialview. Well you can use templated helper by creating strongly typed partialview in “~/Views/Shared/ EditorTemplates” and achieve the same result with less code by using the built-in Html.EditorFor() or Html.EditorForModel() helpers, but in our case it would be difficult to use as we add and remove rows dynamically from the DOM.

All-right coming to the point, Index must be in sequence when posting the form with list of complex object, otherwise MVC DefaultModelBinder can’t able to bind it to our complex object (In our case its List<Student>). Okay let’s see it practically. As you see in above image, “Add Student” button which dynamically add our partialview (row) to DOM using jquery. Below is the code snippet.

$('#lnkAddStudent').click(function() {
	var index = parseInt($('.form-horizontal:last').data('rownum')) + 1;

	$.ajax({
		url: this.href + '/' + index,
		type: "GET",
		cache: false,
		success: function(html) {
			$('.form-horizontal:last').after(html);
		}
	});
	return false;
});

When you click on “Add button”, it makes AJAX request and call “LoadBlankFormView” action method which append partialviews’ html to DOM.

public ActionResult LoadBlankFormView(int id)
{
	return PartialView("_PartialAddStudent", id);
}

Now run the application and click on “Add Student” button 2 times and then fill the form and submit the form by click on “Save” button.

StudentFormWithData

As there is no broken index so DefaultModelBinder binds our complex object correctly when you debug the application, you can see all records posted back correctly as show in below image.

FormPostWithProperIndex

Now let’s remove any of the row which break the index and makes the index as 0, 2, ..etc and our HTML in DOM looks like this –

<form action="/" method="post">        
<div class="col-md-4">
    <h2>Add Student</h2>
	<div>
		<div class="form-group">
			Name
			<div class="col-md-10">
				<input class="form-control" name="Students[0].StudentName" type="text" value="John Miller" />
			</div>
		</div>
		<div class="form-group">
			Age
			<div class="col-md-10">
				<input class="form-control" name="Students[0].Age" type="text" value="65" />
			</div>
		</div>
		<hr />
	</div>
	<div>
		<div class="form-group">
			Name
			<div class="col-md-10">
				<input class="form-control" name="Students[2].StudentName" type="text" value="Krishn" />
			</div>
		</div>
		<div class="form-group">
			Age
			<div class="col-md-10">
				<input class="form-control" name="Students[2].Age" type="text" value="90" />
			</div>
		</div>
	</div>
</div>
</form>

See the index in above code. Here is the “Remove” button code which removes row from the DOM.

$(document.body).on("click", "a.deleteStudent", function() {
	$(this).parents('div.form-horizontal:first').remove();
	return false;
});

Now when you post the above form with broken indices, you’ll lost the data from where the sequence get break. See the below image.

FormPostWithBreakIndex

Don’t worry, to overcome this problem we just need an extra hidden input field with the name – “list/collectionName.Index” for each item we need to bind to the list. In our case hidden input field name should be “Students.Index”.

<input name="Students.Index" type="hidden" value="@Model" />

The name of each of these hidden inputs must be the same with unique values. So after adding it to our “_PratialAddStudent” view, run application again and add 2 new records, remove any middle one and all. After doing all of these test cases at the end, look at rendered html code with broken sequence now looks like this –

ViewSourceOfBrokenIndices

Now click on “Save” button and post that form. Even with broken sequence DefaultModelBinder bind your form’s value correctly!!. You can download the sample demo project here. If you have any query then mail me or leave your comments.

That’s it guys for now. Hope you enjoy this post. Thank you.