Tuesday 29 January 2013

The ASP.NET WebGrid (ASP.NET MVC) (25 min)


The ASP.NET WebGrid (ASP.NET MVC) (25 min)
Tools required: (minimum)
-  Microsoft SQL Server express

We usually have to build web applications that need to present large chunks of data to the user in a tabular fashion in a way that offers the end user a pleasant experience while interacting with our application. Although there are open source and licensed products, I’d like to present in this post a data grid that takes very little effort to do the job: WebGrid.
I am going guide you through the process of creating a web page with a grid to present a quiet short chunk of data in a decent way while taking care of pagination, ordering and filtering.
1)   We will start by creating a new ASP.NET MVC4 Web Application: WebGridDemo.

2)  From Installed Templates select Visual C# > ASP.NET MVC4 Web Application > Click OK and then select template: Basic (An basic ASP.NET MVC 4 project)

Please don’t change the view engine unless you’d like to have messy views. Create a Unit Test project? Up to you.

 


3)  Give it a moment… done. We can now create our default controller: HomeController. Right click on Controllers folder, select Add >>> Controller… option.
Template: Empty MVC controller



4)  We’ll do now some set up work. To make it a bit realistic we will create a simple database with a table from where we are going to pull the data for our grid.
Open Microsoft SQL Server Management Studio and execute the following script. We are creating a Database and a table with some data to play with in our grid.

USE [master]
GO

CREATE DATABASE [TestDb] ON  PRIMARY
( NAME = N'TestDb', FILENAME = N'C:\MyProjects\Databases\TestDb.mdf' , SIZE = 2048KB , MAXSIZE = UNLIMITED, FILEGROWTH = 1024KB )
 LOG ON
( NAME = N'TestDb_log', FILENAME = N'C:\MyProjects\Databases\TestDb_log.ldf' , SIZE = 1024KB , MAXSIZE = 2048GB , FILEGROWTH = 10%)
GO


USE [TestDb]
GO

CREATE TABLE [dbo].[Persons](
      [PersonId] [int] IDENTITY(1,1) NOT NULL,
      [Name] [nvarchar](100) NOT NULL,
      [Email] [nvarchar](100) NOT NULL,
      [Phone] [nvarchar](50) NOT NULL,
      [BirthDate] [datetime] NOT NULL,
      [HasFacebook] [bit] NOT NULL,
 CONSTRAINT [PK_Persons] PRIMARY KEY CLUSTERED
(
      [PersonId] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

INSERT INTO Persons( Name, Email, Phone, BirthDate, HasFacebook)
SELECT 'Chilly Willy', 'Chilly@email.com', '905-234-1234', CAST( '01/28/2000' AS DATETIME), 1
UNION SELECT 'William Hanna', 'Hanna@email.com', '905-234-0001', CAST( '01/01/1990' AS DATETIME), 1
UNION SELECT 'Fred Flintstone', 'Fred@email.com', '905-234-0002', CAST( '01/01/1990' AS DATETIME), 1
UNION SELECT 'Wilma Flintstone', 'Wilma@Flintstone.com', '905-234-0002', CAST( '01/01/1990' AS DATETIME), 1
UNION SELECT 'Mazinger Z', 'ZMazinger@email.com', '905-234-1235', CAST( '01/28/1988' AS DATETIME), 0
UNION SELECT 'Inspector Clouseau', 'Clouseau@email.com', '905-234-0001', CAST( '01/01/1990' AS DATETIME), 0
UNION SELECT 'Mario Test', 'Mario@email.com', '905-234-0002', CAST( '01/01/1990' AS DATETIME), 1
UNION SELECT 'Maria Test', 'Maria@Flintstone.com', '905-234-0002', CAST( '01/01/1990' AS DATETIME), 1
UNION SELECT 'Tom Cat', 'Tom@email.com', '905-234-0002', CAST( '01/01/1990' AS DATETIME), 1
UNION SELECT 'Jerry Mouse', 'Jerry@Flintstone.com', '905-234-0002', CAST( '01/01/1990' AS DATETIME), 1
UNION SELECT 'Yogi Bear', 'Yogi@Flintstone.com', '905-234-0002', CAST( '01/01/1990' AS DATETIME), 1
UNION SELECT 'Quan Chi', 'Yogi@Flintstone.com', '905-234-0002', CAST( '01/01/1990' AS DATETIME), 0
UNION SELECT 'Chilly Willy1', 'Chilly1@email.com', '905-234-1234', CAST( '01/28/2000' AS DATETIME), 1
UNION SELECT 'William Hanna1', 'Hanna1@email.com', '905-234-0001', CAST( '01/01/1990' AS DATETIME), 1
UNION SELECT 'Fred Flintstone1', 'Fred1@email.com', '905-234-0002', CAST( '01/01/1990' AS DATETIME), 1
UNION SELECT 'Wilma Flintstone1', 'Wilma1@Flintstone.com', '905-234-0002', CAST( '01/01/1990' AS DATETIME), 1
UNION SELECT 'Mazinger Z1', 'ZMazinger1@email.com', '905-234-1235', CAST( '01/28/1988' AS DATETIME), 0
UNION SELECT 'Inspector Clouseau1', 'Clouseau1@email.com', '905-234-0001', CAST( '01/01/1990' AS DATETIME), 0
UNION SELECT 'Mario Test1', 'Mario1@email.com', '905-234-0002', CAST( '01/01/1990' AS DATETIME), 1
UNION SELECT 'Maria Test1', 'Maria1@Flintstone.com', '905-234-0002', CAST( '01/01/1990' AS DATETIME), 1
UNION SELECT 'Tom Cat1', 'Tom1@email.com', '905-234-0002', CAST( '01/01/1990' AS DATETIME), 1
UNION SELECT 'Jerry Mouse1', 'Jerry1@Flintstone.com', '905-234-0002', CAST( '01/01/1990' AS DATETIME), 1
UNION SELECT 'Yogi Bear1', 'Yogi1@Flintstone.com', '905-234-0002', CAST( '01/01/1990' AS DATETIME), 1
UNION SELECT 'Quan Chi1', 'Quan1@Flintstone.com', '905-234-0002', CAST( '01/01/1990' AS DATETIME), 0
UNION SELECT 'Chilly Willy2', 'Chilly1@email.com', '905-234-1234', CAST( '01/28/2000' AS DATETIME), 1
UNION SELECT 'William Hanna2', 'Hanna1@email.com', '905-234-0001', CAST( '01/01/1990' AS DATETIME), 1
UNION SELECT 'Fred Flintstone2', 'Fred1@email.com', '905-234-0002', CAST( '01/01/1990' AS DATETIME), 1
UNION SELECT 'Wilma Flintstone2', 'Wilma1@Flintstone.com', '905-234-0002', CAST( '01/01/1990' AS DATETIME), 1
UNION SELECT 'Mazinger Z2', 'ZMazinger1@email.com', '905-234-1235', CAST( '01/28/1988' AS DATETIME), 0
UNION SELECT 'Inspector Clouseau2', 'Clouseau1@email.com', '905-234-0001', CAST( '01/01/1990' AS DATETIME), 0
UNION SELECT 'Mario Test2', 'Mario1@email.com', '905-234-0002', CAST( '01/01/1990' AS DATETIME), 1
UNION SELECT 'Maria Test2', 'Maria1@Flintstone.com', '905-234-0002', CAST( '01/01/1990' AS DATETIME), 1
UNION SELECT 'Tom Cat2', 'Tom1@email.com', '905-234-0002', CAST( '01/01/1990' AS DATETIME), 1
UNION SELECT 'Jerry Mouse2', 'Jerry1@Flintstone.com', '905-234-0002', CAST( '01/01/1990' AS DATETIME), 1
UNION SELECT 'Yogi Bear2', 'Yogi1@Flintstone.com', '905-234-0002', CAST( '01/01/1990' AS DATETIME), 1
UNION SELECT 'Quan Chi2', 'Quan1@Flintstone.com', '905-234-0002', CAST( '01/01/1990' AS DATETIME), 0


5)  To be able to use WebGrid we need to add a reference to our project. Right click on References and select Add Reference…



Browse for the following reference and click OK when found it:




6)  You might also need to open the Web.Config and add the assembly highlighted in the following picture.



7)  Now lets add The Entity Framework, Right click on the project, Select add >>> New Item

Select Data >>> ADO.NET Entity Data Model >>> Click Add button



Generate from Database >>> Add a new Connection to TestDb. >>> Click Next >>> Include Tables in your model >> Click Finish Button



8)  We are going to add our model to transport our list of persons to the view that will show the person’s grid to the user. Right click on the folder named Models and select Class... >>> name: PersonModel.cs >>> Click Add Button.



9)  Replace the current class in the PersonModel with the following code:

namespace WebGridDemo.Models
{
    public class PersonModel
    {
        public List<Person> Persons { get; set; }
        public PagingModel Paging { get; set; }
    }

    public class PagingModel
    {
        public int Total { get; set; }
        public int CurrentPage { get; set; }
        public bool HasMorePages { get; set; }
    }
}

10)We have a Controller and a Model, now we need to create our View. Let’s open our HomeController and replace the code of the only  method in our controller (named Index) with the following code:

        public ActionResult Index(int page=1, int pageSize=5)
        {
            var model = new PersonModel() { Paging = new PagingModel()};
           
            var ef = new TestDbEntities();

            model.Persons = ef.Persons.OrderBy(p=>p.PersonId).Skip((page - 1) * pageSize).Take(pageSize).ToList();
            model.Paging.Total = ef.Persons.Count();
            model.Paging.CurrentPage = page;
            model.Paging.HasMorePages = page * pageSize < model.Paging.Total;

            return View(model);
        }
      
      Note: Resolve errors. (Right click on the red-highlighted errors and select resolve with the proper using statement)

11)Right click on the method you just added (the Index Action Method) and select Add View…



12) Replace the code generated for you in the view with the following:
Note: Take a look how easy the grid is set up and its pagination.
@model WebGridDemo.Models.PersonModel

@{
    ViewBag.Title = "Index";
    Layout = "~/Views/Shared/_Layout.cshtml";
    var personsWebGrid = new WebGrid(Model.Persons);
}

@personsWebGrid.GetHtml(
    htmlAttributes: new {@class="responsive"},
    alternatingRowStyle: "alt",
    columns:
        personsWebGrid.Columns
        (
            personsWebGrid.Column("Person ID", format: @<text>@Html.ActionLink((string)item.PersonId.ToString(), "Detail", new { PersonId = item.PersonId }, new { @class="tiny button" })</text>),
            personsWebGrid.Column("Name", "Name"),
            personsWebGrid.Column("Birth Date", "Birth Date", format: @<text>@item.BirthDate.ToString("dd/MM/yyyy")</text>),
            personsWebGrid.Column("Email", "Email"),
            personsWebGrid.Column("Phone", "Appointment Type"),
            personsWebGrid.Column("HasFacebook", "Has Facebook", format: @<i class="foundicon-flag"></i>)
        ))

@*you can create either a helper to do this or a partial view*@

<ul class="pagination">
@{
    if (Model.Paging.CurrentPage > 1)
    {
        <li>
        @Html.ActionLink("«", "Index", new { page = (Model.Paging.CurrentPage - 1) }, new { @class = "arrow unavailable" });
        </li>
    }

    int position = Model.Paging.CurrentPage % 5;   
    int startPage = Model.Paging.CurrentPage - (position==0?5:position)+1;
    for( int p=startPage; p<startPage+5; p++)
    {
        if (p<= (Model.Paging.Total/5))
        {
            <li>
            @Html.ActionLink(p.ToString(), "Index", new { page = p }, new { @class = (p == Model.Paging.CurrentPage ? "current" : "") });
            </li>
        }
    }
   
    if (Model.Paging.HasMorePages)
    {
        <li>
        @Html.ActionLink("»", "Index", new { page = (Model.Paging.CurrentPage + 1) }, new { @class = "arrow" });
        </li>
    }   
}
</ul>

13)Add a new Action Method in HomeController. The following code represents the Action method that will be used to fetch a specific person selected from the grid.
You will find that the grid has in the first column a maybe “weird” button when clicked, fetches a person by its Id and displays it a new page. The idea was to show that you can set up other things in the grid (checkboxes, dropdowns, images, etc)

        public ActionResult Detail(int personId)
        {
            var ef = new TestDbEntities();

            var model = ef.Persons.Where(p => p.PersonId == personId).FirstOrDefault();

            return View(model);
        }


14)Add the View that is going to be returned by the action method you just added.



15)Replace the code generated for you in the View with the following:

@model WebGridDemo.Person

@{
    ViewBag.Title = "Detail";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>Detail</h2>

<fieldset>
    <legend>Person</legend>

    <div class="display-label">
         @Html.DisplayNameFor(model => model.Name)
    </div>
    <div class="display-field">
        @Html.DisplayFor(model => model.Name)
    </div>

    <div class="display-label">
         @Html.DisplayNameFor(model => model.Email)
    </div>
    <div class="display-field">
        @Html.DisplayFor(model => model.Email)
    </div>

    <div class="display-label">
         @Html.DisplayNameFor(model => model.Phone)
    </div>
    <div class="display-field">
        @Html.DisplayFor(model => model.Phone)
    </div>

    <div class="display-label">
         @Html.DisplayNameFor(model => model.BirthDate)
    </div>
    <div class="display-field">
        @Html.DisplayFor(model => model.BirthDate)
    </div>

    <div class="display-label">
         @Html.DisplayNameFor(model => model.HasFacebook)
    </div>
    <div class="display-field">
        @Html.DisplayFor(model => model.HasFacebook)
    </div>
</fieldset>
<p>
    @Html.ActionLink("Back to List", "Index", new { id=Model.PersonId },  new { @class = "small button" })
</p>


16)I kind of became fan of foundation (from Zurb – the most advanced responsive front-end framework in the world…). So we are going to download the package to make use of some of its features: Click here  to download.

17)   Extract (unzip) the files and copy the three folders highlighted into your project as shown in the following image.

 

18)Now include those folders to your project. First click the icon “Show All Files” and then right click on the folders that need to be added and select include in project.



19)Go to the folder Views >>> Shared and open the file _layout.cshtml and replace the current code with the following:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>@ViewBag.Title</title>
       
        <!-- Set the viewport width to device width for mobile -->
        <meta content="width=device-width" name="viewport">
   
      <link rel="stylesheet" href="~/stylesheets/foundation.min.css"/>
      <link rel="stylesheet" href="~/stylesheets/app.css"/>

      <script type="text/javascript" src="~/javascripts/modernizr.foundation.js"></script>
 
      <!-- IE Fix for HTML5 Tags -->
      <!--[if lt IE 9]>
        <script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
      <![endif]-->

    </head>
    <body>

        <div class="container top-bar home-border">
            <div class="attached">
                <div class="name">
                    <span><a href="#">the WebGrid Demo </a>
                    </span>
                      </div>

               </div>
        </div>

      <div class="row">
        <div class="twelve columns">
          <h3>The WebGrid in MVC.</h3>
          <hr>
        </div>
      </div>

      <div class="row">
        <div class="twelve columns">
                @RenderBody()
        </div>
      </div>

        <script type="text/javascript" src="~/javascripts/jquery.js"></script>
        <script type="text/javascript" src="~/javascripts/foundation.min.js"></script>
 
        <!-- Initialize JS Plugins -->
        <script type="text/javascript" src="~/javascripts/app.js"></script>


    </body>

</html>

20) Press F5. Your grid should look like the one presented in the following img.




Considerations
The trip in the train seemed to be shorter this time to include sorting and filtering. I’ll continue with it soon.