Saturday, 3 September 2016

Datatables is quite popular JQuery based plugin. There are a lot of flavors of Datatables plugin and it supports many major web programming technologies.
For todays blog, I shall be focusing more on How Datatables plugin can be integrated with classic ASP.NET webform server side data. If anyone is interested to integrate this beautiful plugin with ASP.NET MVC5 server then here is the link



Following are some prerequisites before you proceed any further in this tutorial:

Prerequisites:
1) Knowledge about classic ASP.NET webform.
2) Knowledge about HTML.
3) Knowledge about Javascript.
4) Knowledge about AJAX.
5) Knowledge about CSS.
6) Knowledge about Bootstrap.
7) Knowledge about C# programming.
8) Knowledge about C# LINQ.
9) Knowledge about JQuery.
10) Knowledge about WebMethod attribute

You can download the complete source code for this tutorial from here or you can follow the step by step discussion below. The sample code is developed in Microsoft Visual Studio 2015 Enterprise. I am using SalesOrderDetail table extract from Adventure Works Sample Database.

Let's begin now.

1) Create new Webform web application project and name it "JqDatatablesWebfoms".
2) Since the project is created with for ".Net 4.5.2" framework therefore you will see its project hierarchy a bit similar to MVC5 hierarchy. If that's the case then you will see "RouteConfig.cs" file, open it and replace it with following code:

     public static void RegisterRoutes(RouteCollection routes)  
     {  
       var settings = new FriendlyUrlSettings();  
       settings.AutoRedirectMode = RedirectMode.Off; // RedirectMode.Permanent  
       routes.EnableFriendlyUrls(settings);  
     }  

In above, I have changed "RedirectMode.Permanent" to "RedirectMode.Off". The reason is that since, I am going to use ajax call in datatables plugin, so, if redirect mode in friendly URL  is not off then my ajax call will redirect to root page and ajax call to serverside will not work.

3) Now, open "Site.Master" replace following code in it:

 <%@ Master Language="C#" AutoEventWireup="true" CodeBehind="Site.master.cs" Inherits="JqDatatablesWebForm.SiteMaster" %>  
 <!DOCTYPE html>  
 <html lang="en">  
 <head runat="server">  
   <meta charset="utf-8" />  
   <meta name="viewport" content="width=device-width, initial-scale=1.0" />  
   <title><%: Page.Title %></title>  
   <asp:PlaceHolder runat="server">  
     <%: Scripts.Render("~/bundles/modernizr") %>  
     <%: Scripts.Render("~/bundles/jquery") %>  
   </asp:PlaceHolder>  
   <webopt:bundlereference runat="server" path="~/Content/css" />  
   <link href="~/favicon.ico" rel="shortcut icon" type="image/x-icon" />  
   <!-- Font Awesome -->  
   <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css" />  
   <!-- Data table -->  
   <link rel="stylesheet" href="https://cdn.datatables.net/1.10.10/css/dataTables.bootstrap.min.css " />  
   <link rel="stylesheet" href="Content/style/custom-style.css" />  
   <!-- Data Table -->  
   <script src="https://cdn.datatables.net/1.10.10/js/jquery.dataTables.min.js" type="text/javascript"></script>  
   <script src="https://cdn.datatables.net/1.10.10/js/dataTables.bootstrap.min.js" type="text/javascript"></script>  
   <script src="Scripts/custom-datatable.js" type="text/javascript"></script>  
 </head>  
 <body>  
   <form runat="server">  
     <asp:ScriptManager runat="server">  
       <Scripts>  
         <%--To learn more about bundling scripts in ScriptManager see http://go.microsoft.com/fwlink/?LinkID=301884 --%>  
         <%--Framework Scripts--%>  
         <asp:ScriptReference Name="MsAjaxBundle" />  
 <%--        <asp:ScriptReference Name="jquery" />--%>  
         <asp:ScriptReference Name="bootstrap" />  
         <asp:ScriptReference Name="respond" />  
         <asp:ScriptReference Name="WebForms.js" Assembly="System.Web" Path="~/Scripts/WebForms/WebForms.js" />  
         <asp:ScriptReference Name="WebUIValidation.js" Assembly="System.Web" Path="~/Scripts/WebForms/WebUIValidation.js" />  
         <asp:ScriptReference Name="MenuStandards.js" Assembly="System.Web" Path="~/Scripts/WebForms/MenuStandards.js" />  
         <asp:ScriptReference Name="GridView.js" Assembly="System.Web" Path="~/Scripts/WebForms/GridView.js" />  
         <asp:ScriptReference Name="DetailsView.js" Assembly="System.Web" Path="~/Scripts/WebForms/DetailsView.js" />  
         <asp:ScriptReference Name="TreeView.js" Assembly="System.Web" Path="~/Scripts/WebForms/TreeView.js" />  
         <asp:ScriptReference Name="WebParts.js" Assembly="System.Web" Path="~/Scripts/WebForms/WebParts.js" />  
         <asp:ScriptReference Name="Focus.js" Assembly="System.Web" Path="~/Scripts/WebForms/Focus.js" />  
         <asp:ScriptReference Name="WebFormsBundle" />  
         <%--Site Scripts--%>  
       </Scripts>  
     </asp:ScriptManager>  
   <div class="navbar navbar-inverse navbar-fixed-top">  
     <div class="container">  
       <div class="navbar-header">  
         <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">  
           <span class="icon-bar"></span>  
           <span class="icon-bar"></span>  
           <span class="icon-bar"></span>  
         </button>  
       </div>  
     </div>  
   </div>  
     <div class="container body-content">  
       <asp:ContentPlaceHolder ID="MainContent" runat="server">  
       </asp:ContentPlaceHolder>  
       <hr />  
       <footer>  
         <center>  
           <p><strong>Copyright &copy; <%: DateTime.Now.Year %> - <a href="http://asmak9.blogspot.com/">Asma's Blog</a>.</strong> All rights reserved.</p>  
         </center>  
       </footer>  
     </div>  
   </form>  
 </body>  
 </html>  

 Here, I have simply altered the existing layout and incorporate links to require scripts and styles.

4) Now, open "Default.aspx" page and replace following code in it:

 <%@ Page Title="ASP.NET Webform: Datatables Jquery Plugin Integration" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="JqDatatablesWebForm._Default" %>  
 <asp:Content ID="BodyContent" ContentPlaceHolderID="MainContent" runat="server">  
   <div class="row">  
     <div class="panel-heading">  
       <div class="col-md-8 custom-heading3">  
         <h3>  
           <i class="fa fa-table"></i>  
           <span>Datatables Jquery Plugin Integration with ASP.NET Webform C#</span>  
         </h3>  
       </div>  
     </div>  
   </div>  
   <div class="row">  
     <section class="col-md-12 col-md-push-0">  
       <section>  
         <div class="well bs-component">  
           <br />  
           <div class="row">  
             <div>  
               <table class="table table-striped table-bordered table-hover"  
                   id="TableId"  
                   cellspacing="0"  
                   align="center"  
                   width="100%">  
                 <thead>  
                   <tr>  
                     <th>Sr</th>  
                     <th>Order Track Number</th>  
                     <th>Quantity</th>  
                     <th>Product Name</th>  
                     <th>Special Offer</th>  
                     <th>Unit Price</th>  
                     <th>Unit Price Discount</th>  
                   </tr>  
                 </thead>  
               </table>  
             </div>  
           </div>  
         </div>  
       </section>  
     </section>  
   </div>  
 </asp:Content>  

Here, I have created a table holder that will be integrated with Datatables plugin with data from server side. I have only provided table header information here, since the data will be integrated from server side.

5) Now create two new models under "Model", name it "SalesOrderDetail.cs" & "DataTables.cs" and add following properties in them:

 using System;  
 using System.Collections.Generic;  
 using System.Linq;  
 using System.Text;  
 using System.Threading.Tasks;  
 namespace PluginIntegration_1.Models  
 {  
   public class SalesOrderDetail  
   {  
     public int Sr { get; set; }  
     public string OrderTrackNumber { get; set; }  
     public int Quantity { get; set; }  
     public string ProductName { get; set; }  
     public string SpecialOffer { get; set; }  
     public double UnitPrice { get; set; }  
     public double UnitPriceDiscount { get; set; }  
   }  
 }  

 using System;  
 using System.Collections.Generic;  
 using System.Linq;  
 using System.Text;  
 using System.Threading.Tasks;  
 namespace JqDatatablesWebForm.Models  
 {  
   public class DataTables  
   {  
     public int draw { get; set; }  
     public int recordsTotal { get; set; }  
     public int recordsFiltered { get; set; }  
     public List<SalesOrderDetail> data { get; set; }  
   }  
 }  

6) Now, In "Default.aspx.cs" file add following function to load data from "SalesOrderDetail.txt" text file:

     #region Load Data  
     /// <summary>  
     /// Load data method.  
     /// </summary>  
     /// <returns>Returns - Data</returns>  
     private List<SalesOrderDetail> LoadData()  
     {  
       // Initialization.  
       List<SalesOrderDetail> lst = new List<SalesOrderDetail>();  
       try  
       {  
         // Initialization.  
         string line = string.Empty;  
         string srcFilePath = "content/files/SalesOrderDetail.txt";  
         var rootPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().CodeBase);  
         var fullPath = Path.Combine(rootPath, srcFilePath);  
         string filePath = new Uri(fullPath).LocalPath;  
         StreamReader sr = new StreamReader(new FileStream(filePath, FileMode.Open, FileAccess.Read));  
         // Read file.  
         while ((line = sr.ReadLine()) != null)  
         {  
           // Initialization.  
           SalesOrderDetail infoObj = new SalesOrderDetail();  
           string[] info = line.Split(',');  
           // Setting.  
           infoObj.Sr = Convert.ToInt32(info[0].ToString());  
           infoObj.OrderTrackNumber = info[1].ToString();  
           infoObj.Quantity = Convert.ToInt32(info[2].ToString());  
           infoObj.ProductName = info[3].ToString();  
           infoObj.SpecialOffer = info[4].ToString();  
           infoObj.UnitPrice = Convert.ToDouble(info[5].ToString());  
           infoObj.UnitPriceDiscount = Convert.ToDouble(info[6].ToString());  
           // Adding.  
           lst.Add(infoObj);  
         }  
         // Closing.  
         sr.Dispose();  
         sr.Close();  
       }  
       catch (Exception ex)  
       {   
         // info.  
         Console.Write(ex);  
       }  
       // info.  
       return lst;  
     }  

The above piece of code simply loads data from text file into list.

7) Now, create new script file under "Scripts" folder, name it "custom-datatable.js" and place following code in it:

 $(document).ready(function ()  
 {  
   $('#TableId').DataTable(  
   {  
     "columnDefs": [  
       { "width": "5%", "targets": [0] },  
       { "className": "text-center custom-middle-align", "targets": [0, 1, 2, 3, 4, 5, 6] },  
     ],  
     "language":  
       {  
         "processing": "<div class='overlay custom-loader-background'><i class='fa fa-cog fa-spin custom-loader-color'></i></div>"  
       },  
     "processing": true,  
     "serverSide": true,  
     "ajax":  
       {  
         "url": "Default.aspx/GetData",  
         "contentType": "application/json",  
         "type": "GET",  
         "dataType": "JSON",  
         "data": function (d)  
         {  
           return d;  
         },  
         "dataSrc": function (json)  
         {  
           json.draw = json.d.draw;  
           json.recordsTotal = json.d.recordsTotal;  
           json.recordsFiltered = json.d.recordsFiltered;  
           json.data = json.d.data;  
           var return_data = json;  
           return return_data.data;  
         }  
       },  
     "columns": [  
           { "data": "Sr" },  
           { "data": "OrderTrackNumber" },  
           { "data": "Quantity" },  
           { "data": "ProductName" },  
           { "data": "SpecialOffer" },  
           { "data": "UnitPrice" },  
           { "data": "UnitPriceDiscount" }  
     ]  
   });  
 });  

Now, this is the fun part which will display the server side data in the table that we have created earlier into our "Default.aspx" page. This is how Datatables plugin integrate server side data with underlying web programming language. Let's see each information here chunk by chunk:

     "columnDefs": [  
       { "width": "5%", "targets": [0] },  
       { "className": "text-center custom-middle-align", "targets": [0, 1, 2, 3, 4, 5, 6] },  
     ],  
 
This chunk of code provides styling, enable/disable information for sorting, searching etc, for number of columns which are being used in the table, which is why this chunk of code defines columns definition for our table.

     "language":  
       {  
         "processing": "<div class='overlay custom-loader-background'><i class='fa fa-cog fa-spin custom-loader-color'></i></div>"  
       },  

This chunk of code allows to customize processing message that will appear when data is being loaded. I have used following custom styling here:

 .custom-loader-color   
 {  
   color: #fff !important;  
   font-size: 50px !important;  
 }  
 .custom-loader-background  
 {  
   background-color: crimson !important;  
 }  
 .custom-middle-align   
 {  
   vertical-align: middle !important;  
 }  

Below is the snippet of how the processing loader will be looked like:



Below piece of code will enable the data loading from server side:

     "processing": true,  
     "serverSide": true,  
     "ajax":  
       {  
         "url": "Default.aspx/GetData",  
         "contentType": "application/json",  
         "type": "GET",  
         "dataType": "JSON",  
         "data": function (d)  
         {  
           return d;  
         },  
         "dataSrc": function (json)  
         {  
           json.draw = json.d.draw;  
           json.recordsTotal = json.d.recordsTotal;  
           json.recordsFiltered = json.d.recordsFiltered;  
           json.data = json.d.data;  
           var return_data = json;  
           return return_data.data;  
         }  
       },  
     "columns": [  
           { "data": "Sr" },  
           { "data": "OrderTrackNumber" },  
           { "data": "Quantity" },  
           { "data": "ProductName" },  
           { "data": "SpecialOffer" },  
           { "data": "UnitPrice" },  
           { "data": "UnitPriceDiscount" }  
     ]  

The columns here are the exact name of the properties that we have created in "SalesOrderDetail.cs" file and the path "Default.aspx/GetData" is the function that will be returning data from server side. "dataSrc" property will format data from server side and package it into format that is acceptable by DataTables plugin.

8) Now, in "Default.aspx.cs" file let's create "GetData" method as follow:

     #region Get data method.  
     /// <summary>  
     /// GET: Default.aspx/GetData  
     /// </summary>  
     /// <returns>Return data</returns>  
     [WebMethod]  
     [ScriptMethod(ResponseFormat = ResponseFormat.Json, UseHttpGet = true)]  
     public static object GetData()  
     {  
       // Initialization.  
       DataTables result = new DataTables();  
       try  
       {  
         // Initialization.  
         string search = HttpContext.Current.Request.Params["search[value]"];  
         string draw = HttpContext.Current.Request.Params["draw"];  
         string order = HttpContext.Current.Request.Params["order[0][column]"];  
         string orderDir = HttpContext.Current.Request.Params["order[0][dir]"];  
         int startRec = Convert.ToInt32(HttpContext.Current.Request.Params["start"]);  
         int pageSize = Convert.ToInt32(HttpContext.Current.Request.Params["length"]);  
         // Loading.  
         List<SalesOrderDetail> data = _Default.LoadData();  
         // Total record count.  
         int totalRecords = data.Count;  
         // Verification.  
         if (!string.IsNullOrEmpty(search) &&  
           !string.IsNullOrWhiteSpace(search))  
         {  
           // Apply search  
           data = data.Where(p => p.Sr.ToString().ToLower().Contains(search.ToLower()) ||  
                       p.OrderTrackNumber.ToLower().Contains(search.ToLower()) ||  
                       p.Quantity.ToString().ToLower().Contains(search.ToLower()) ||  
                       p.ProductName.ToLower().Contains(search.ToLower()) ||  
                       p.SpecialOffer.ToLower().Contains(search.ToLower()) ||  
                       p.UnitPrice.ToString().ToLower().Contains(search.ToLower()) ||  
                       p.UnitPriceDiscount.ToString().ToLower().Contains(search.ToLower())).ToList();  
         }  
         // Sorting.  
         data = _Default.SortByColumnWithOrder(order, orderDir, data);  
         // Filter record count.  
         int recFilter = data.Count;  
         // Apply pagination.  
         data = data.Skip(startRec).Take(pageSize).ToList();  
         // Loading drop down lists.  
         result.draw = Convert.ToInt32(draw);  
         result.recordsTotal = totalRecords;  
         result.recordsFiltered = recFilter;  
         result.data = data;  
       }  
       catch (Exception ex)  
       {  
         // Info  
         Console.Write(ex);  
       }  
       // Return info.  
       return result;  
     }  
     #endregion  

In above piece of code, we have created a WebMethod which is based on searching, sorting and pagination information sent from Datatebles plugin, following have been done i.e.

1) Data is being loaded first.
2) Data is being churned out base on searching criteria.
3) Data is sorted by provided column in provided order.
4) Data is then paginated.
5) Data is returned.

"GetData" function will be executed each time the table is being searched, sort or new page is accessed. Here are following two lines which are important:

         // Total record count.  
         int totalRecords = data.Count;  

         // Filter record count.  
         int recFilter = data.Count;  

First line determines the actual amount of records that exist in the list and second line determines the amount of records that are left after applying filtering. Below is the piece of code that will do the sorting:

     #region Sort by column with order method  
     /// <summary>  
     /// Sort by column with order method.  
     /// </summary>  
     /// <param name="order">Order parameter</param>  
     /// <param name="orderDir">Order direction parameter</param>  
     /// <param name="data">Data parameter</param>  
     /// <returns>Returns - Data</returns>  
     private List<SalesOrderDetail> SortByColumnWithOrder(string order, string orderDir, List<SalesOrderDetail> data)  
     {  
       // Initialization.  
       List<SalesOrderDetail> lst = new List<SalesOrderDetail>();  
       try  
       {  
         // Sorting  
         switch (order)  
         {  
           case "0":  
             // Setting.  
             lst = orderDir.Equals("DESC", StringComparison.CurrentCultureIgnoreCase) ? data.OrderByDescending(p => p.Sr).ToList()  
                                                  : data.OrderBy(p => p.Sr).ToList();  
             break;  
           case "1":  
             // Setting.  
             lst = orderDir.Equals("DESC", StringComparison.CurrentCultureIgnoreCase) ? data.OrderByDescending(p => p.OrderTrackNumber).ToList()  
                                                  : data.OrderBy(p => p.OrderTrackNumber).ToList();  
             break;  
           case "2":  
             // Setting.  
             lst = orderDir.Equals("DESC", StringComparison.CurrentCultureIgnoreCase) ? data.OrderByDescending(p => p.Quantity).ToList()  
                                                  : data.OrderBy(p => p.Quantity).ToList();  
             break;  
           case "3":  
             // Setting.  
             lst = orderDir.Equals("DESC", StringComparison.CurrentCultureIgnoreCase) ? data.OrderByDescending(p => p.ProductName).ToList()  
                                                  : data.OrderBy(p => p.ProductName).ToList();  
             break;  
           case "4":  
             // Setting.  
             lst = orderDir.Equals("DESC", StringComparison.CurrentCultureIgnoreCase) ? data.OrderByDescending(p => p.SpecialOffer).ToList()  
                                                   : data.OrderBy(p => p.SpecialOffer).ToList();  
             break;  
           case "5":  
             // Setting.  
             lst = orderDir.Equals("DESC", StringComparison.CurrentCultureIgnoreCase) ? data.OrderByDescending(p => p.UnitPrice).ToList()  
                                                  : data.OrderBy(p => p.UnitPrice).ToList();  
             break;  
           case "6":  
             // Setting.  
             lst = orderDir.Equals("DESC", StringComparison.CurrentCultureIgnoreCase) ? data.OrderByDescending(p => p.UnitPriceDiscount).ToList()  
                                                  : data.OrderBy(p => p.UnitPriceDiscount).ToList();  
             break;  
           default:  
             // Setting.  
             lst = orderDir.Equals("DESC", StringComparison.CurrentCultureIgnoreCase) ? data.OrderByDescending(p => p.Sr).ToList()   
                                                  : data.OrderBy(p => p.Sr).ToList();  
             break;  
         }  
       }  
       catch (Exception ex)  
       {  
         // info.  
         Console.Write(ex);  
       }  
       // info.  
       return lst;  
     }  
     #endregion  

Here how the results will look like after applying the filtering:




That's about it.

Enjoy!! Coding.

2 comments:

  1. Your coding examples are really excellent and i am looking forward more things from your blog, so please say about it.

    Best Implant Clinic In Chennai | Best Laser Clinic In Chennai | Best Dental Clinic In Vellore

    ReplyDelete
  2. Thank you for your kind comments. More stuff is coming soon.

    ReplyDelete