Click here to Skip to main content
Click here to Skip to main content

MvcContrib Grid Paging and Searching in ASP.NET MVC3

By , 4 May 2011
 

Introduction

This article shows you how to implement MvcContrib grid paging, filtering and preserving search URL in ASP.NET MVC3.

Background

MvcContrib grid provides nice paging interface with column ordering. In the real world, we need to aggregate complex ViewModels to present grid view. We should also provide multiple searching filters and keywords to the grid view.

So, I implemented clean PagedViewModel<T> class to make MvcContrib Grid paging and filtering simple in ASP.NET MVC3, based on this nice article.

Using the Code

Summary

I added some code and classes to the original source to simplify implementation as follows:

  • PagedViewModel<T> containing IPagenation<T>, AddFilter methods and sorting/paging information
  • Preserving the query URL between list page and view page

Business Layer

Let's use the music store database of http://chinookdatabase.codeplex.com/ for our AlbumServie. (*I attached the mdf with basic connection string in web.config in our MVC project.)

Let's add MVCMusicStoreDB EF4 model including Album, Genre and Artist tables. We can see the basic entity diagram.

The AlbumViewModel class is the composite ViewModel class of Album, Genre and Artist entities. The MvcContrib Grid uses those Display* and ScaffoldColumn attributes in the AutoGenerateColumns() function.

using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
namespace MvcMusicStore.Models
{
    public class AlbumViewModel
    {
        [DisplayName("ID")]
        public int AlbumId { get; set; }
        [ScaffoldColumn(false)]
        public int? GenreId { get; set; }
        [DisplayName("Genre")]
        public string Genre { get; set; }
        [ScaffoldColumn(false)]
        public int? ArtistId { get; set; }
        [DisplayName("Artist")]
        public string Artist { get; set; }
        [ScaffoldColumn(false)]
        public string AlbumTitle { get; set; }
        [DisplayName("Price")]
        [DisplayFormat(DataFormatString = "{0:c}")] 
        public decimal AlbumPrice { get; set; }
    }
}

Let's build our main business service methods. The first step is adding some basic reading methods in AlbumService. We don't implement CUD methods and any UoW or Repository layer for simple & quick implementation.

namespace MvcMusicStore.Models
{
    public class AlbumService
    {
        private MvcMusicStoreEntities _context;
        public AlbumService()
        {
            _context = new MvcMusicStoreEntities();
        }
        public IQueryable<AlbumViewModel> GetAlbumsView()
        {
            var query = from a in GetAlbums()
                        select new AlbumViewModel
                        {
                            AlbumId = a.AlbumId,
                            GenreId = a.GenreId,
                            Genre = a.Genre.Name,
                            ArtistId = a.ArtistId,
                            Artist = a.Artist.Name,
                            AlbumTitle = a.Title,
                            AlbumPrice = a.Price
                        };
            return query;
        }
        public AlbumViewModel FindAlbumView(int albumId)
        {
            return GetAlbumsView().Where(a => a.AlbumId == albumId).Single();
        }
        public IQueryable<Album> GetAlbums()
        {
            return _context.Albums;
        }
        public IQueryable<Genre> GetGenres()
        {
            return _context.Genres;
        }
        public IQueryable<Artist> GetArtists()
        {
            return _context.Artists;
        }
        public void Save()
        {
            _context.SaveChanges();
        }
    }
}        

PagedViewModel<T>

PagedViewModel<T> is the generic container holding all data for grid presentation including search filters. There are also several AddFilter implementations that register simple filters like search keyword and SelectListFilterViewItem is for saving SelectList object with adding viewdata dictionay.

To support fluent filter setting function to the PagedViewModel<T>, we should implement AddFilter and Setup methods.

namespace MvcMusicStore.Models
{
    public class PagedViewModel<T>
    {   ...
        public PagedViewModel<T> AddFilter(Expression<Func<T, bool>> predicate)
        {
            Query = Query.Where(predicate);
            return this;
        }
        public PagedViewModel<T> AddFilter<TValue>
	(string key, TValue value, Expression<Func<T, bool>> predicate)
        {
            ProcessQuery(value, predicate);
            ViewData[key] = value;
            return this;
        }
        public PagedViewModel<T> AddFilter<TValue>
	(string keyField, object value, Expression<Func<T, bool>> predicate,
            IQueryable<TValue> query, string textField)
        {
            ProcessQuery(value, predicate);
            var selectList = query.ToSelectList(keyField, textField, value);
            ViewData[keyField] = selectList;
            return this;
        }
        public PagedViewModel<T> Setup()
        {
            if (string.IsNullOrWhiteSpace(GridSortOptions.Column))
            {
                GridSortOptions.Column = DefaultSortColumn;
            }
            PagedList = Query.OrderBy
		(GridSortOptions.Column, GridSortOptions.Direction)
                .AsPagination(Page ?? 1, PageSize ?? 10);
            return this;
        }
        private void ProcessQuery<TValue>
		(TValue value, Expression<Func<T, bool>> predicate)
        {
            if (value == null) return;
            if (typeof(TValue) == typeof(string))
            {
                if (string.IsNullOrWhiteSpace(value as string)) return;
            }
            Query = Query.Where(predicate);
        }
    }
}

Listing Action Method in Controller

Let's make a listing method of AlbumController with searching filters, ordering and paging in our AlbumController. We can add fluently query filters to the filter pipeline of PagedViewModel.

namespace MvcMusicStore.Controllers 
{ 
    public class AlbumController : Controller 
    { 
        private AlbumService _service; 
        public AlbumController() 
        { 
            _service = new AlbumService(); 
        } 
        public ActionResult Index(string albumTitle, 
	int? genreId, int? artistId, GridSortOptions gridSortOptions, int? page) 
        { 
            var pagedViewModel = new PagedViewModel<AlbumViewModel> 
            { 
                ViewData = ViewData, 
                Query = _service.GetAlbumsView(), 
                GridSortOptions = gridSortOptions, 
                DefaultSortColumn = "AlbumId", 
                Page = page, 
            } 
            .AddFilter("albumTitle", albumTitle, 
			a => a.AlbumTitle.Contains(albumTitle)) 
            .AddFilter("genreId", genreId, 
		a => a.GenreId == genreId, _service.GetGenres(), "Name") 
            .AddFilter("artistId", artistId, 
		a => a.ArtistId == artistId, _service.GetArtists(), "Name") 
            .Setup(); 
            return View(pagedViewModel); 
        } 
        ... 
    } 
}

Listing Razor View Page for MvcContrib Grid

Let's make List View Page. We can easily fill two dropdownlists without any additional code from PagedViewModel<T>. Also, we add a link using Html.ActionQueryLink helper method to keep query string like "/Album/Details/420?albumTitle=Ro&amp;genreId=1".

@using MvcMusicStore.Common 
@using MvcMusicStore.Models; 
@using MvcContrib.UI.Grid; 
@model PagedViewModel<AlbumViewModel> 
@{ 
    ViewBag.Title = "Album List"; 
}
<h2>Album List</h2> 
@using (Html.BeginForm("Index", "Album", 
	FormMethod.Get, new { id = "albumSearch" })) 
{    
<label> 
Title   @Html.TextBox("albumTitle") 
  Genre @Html.DropDownList("genreId", "-- Select All --") 
</label> 
<label> 
Artist @Html.DropDownList("artistId", "-- Select All --") 
  <input class="button" value="Search" type="submit" /> 
</label> 
}
@{Html.RenderPartial("Pager", Model.PagedList);} 
@Html.Grid(Model.PagedList).AutoGenerateColumns().Columns(
column =) {column.For(x =) Html.ActionQueryLink(x.AlbumTitle, "Details", 
new { id = x.AlbumId })).Named("AlbumTitle").InsertAt(2);
}).Sort(Model.GridSortOptions).Attributes(@class =) "grid-style")

Let's see SearchBox with title keyword, Genre & Album Dropdownlist. The contrib Grid provides Column filtering and paging UI.

We can go back to the List page with preserved query option from Details page. Let's add a snippet to save routing URL to ViewBag by using added ToRouteDic extension method.

namespace MvcMusicStore.Controllers
{
    public class AlbumController : Controller
    {   ...
        public ActionResult Details(int id)
        {
            var viewModel = _service.FindAlbumView(id);
            ViewBag.RouteDicForList = Request.QueryString.ToRouteDic();
            return View(viewModel);
        }
    }
}

Let's restore the ViewBag data to restore the query URL in Details Viewpage.

@model MvcMusicStore.Models.AlbumViewModel
@{
    ViewBag.Title = "Details";
}
<h2>Album Details - @Model.AlbumTitle</h2>
<p>
    @Html.ActionLink("Back to List", "Index", 
    ViewBag.RouteDicForList as RouteValueDictionary)
</p>

Conclusion

MvcContrib Grid is a nice web grid component for MVC3 framework. We can use great paging, filtering, ordering grid functions easily.

References

License

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

About the Author

Sangsu Park 99
Software Developer (Senior)
Australia Australia
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
Hint: For improved responsiveness ensure Javascript is enabled and choose 'Normal' from the Layout dropdown and hit 'Update'.
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 5memberArmando Airo'10 Apr '13 - 6:07 
QuestionAjax pagermemberLluthus14 Mar '13 - 4:05 
GeneralMy vote of 5memberab_thai15 Jan '13 - 17:05 
Generalnice onememberMember 382310125 Nov '12 - 19:35 
QuestionGet duplicate Search form after postmemberkhanhqhoang30 Oct '12 - 12:20 
GeneralMy vote of 5memberpablopecora9 Aug '12 - 19:55 
SuggestionDo you have the code for add/update/delete?membergogsthecoder19 Jul '12 - 3:58 
QuestionMy vote is 5memberZ@clarco17 Jul '12 - 23:20 
GeneralRe: My vote is 5memberSangsu Park 9918 Jul '12 - 0:25 
QuestionAnswers to some questions, over this example, on demand, custom sorting and pagingmemberAndrés Meza21 May '12 - 17:46 
QuestionMy veto is 0memberDelphy9 Apr '12 - 0:25 
AnswerRe: My veto is 0memberNabin Kumar Jha11 May '12 - 0:40 
Questionerror CS0433 about ModelClientValidationRule in Models\AccountModels.cs(242,43)membersheir23 Mar '12 - 8:14 
QuestionPagination issuememberDimitri Backaert23 Mar '12 - 5:06 
GeneralMy vote of 5memberkishor Shinde23 Feb '12 - 20:13 
QuestionCreating a ViewmemberMember 866805523 Feb '12 - 19:43 
QuestionAwesome Articlememberzyck18 Feb '12 - 20:22 
AnswerRe: Awesome ArticlememberSangsu Park 9923 Feb '12 - 7:37 
QuestionGood example, but how can I add "Create New", "Edit" and "Delete" functions?memberpeter cong18 Feb '12 - 4:29 
AnswerRe: Good example, but how can I add "Create New", "Edit" and "Delete" functions?memberSangsu Park 9923 Feb '12 - 7:36 
GeneralGreate workmemberDhol Gaurav5 Jan '12 - 8:11 
GeneralRe: Greate workmemberSangsu Park 995 Jan '12 - 12:46 
Questionit has problem when i use filter and paging togethermemberfunlive14 Aug '11 - 0:12 
AnswerRe: it has problem when i use filter and paging togethermemberSangsu Park 9914 Aug '11 - 1:44 
GeneralRe: it has problem when i use filter and paging togethermemberfunlive14 Aug '11 - 4:18 

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

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130516.1 | Last Updated 4 May 2011
Article Copyright 2011 by Sangsu Park 99
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid