Deborah's Developer MindScape






         Tips and Techniques for Web and .NET developers.

January 25, 2010

ASP.NET: GridView and Business Objects

Filed under: ASP.NET,C#,VB.NET @ 12:53 am

Most ASP.NET GridView control examples demonstrate using the GridView with a SQLDataSource. But in some cases, you may want to use your own business objects instead.

One way to achieve this goal is to use the ObjectDataSource as shown in MSDN here.

Another option is to simply bind the GridView directly to your business objects without using a DataSource control.

The example presented in this post uses business objects you build yourself. These "home made" business objects are often referred to as POCO, or "plain old CLR objects". Use the techniques presented in this post any time you want to use your business objects in a GridView without using a DataSource control.

Prerequisites

First, we need some business objects. This example uses a Customer class that defines a single customer, and a Customers (plural) class that returns a generic list of customers.

In C#:

public class Customer
{
    public int CustomerId { get; set; }
    public string LastName { get; set; }
    public string FirstName { get; set; }
    public string EmailAddress { get; set; }
}

public class Customers
{
   public static List<Customer> Retrieve()
   {
      List<Customer> custList = new List<Customer>
                    {new Customer()
                          { CustomerId = 1,
                            FirstName="Bilbo",
                            LastName = "Baggins",
                            EmailAddress = "bb@hob.me"},
                    new Customer()
                          { CustomerId = 2,
                            FirstName="Frodo",
                            LastName = "Baggins",
                            EmailAddress = "fb@hob.me"},
                    new Customer()
                          { CustomerId = 3,
                            FirstName="Samwise",
                            LastName = "Gamgee",
                            EmailAddress = "sg@hob.me"},
                    new Customer()
                          { CustomerId = 4,
                            FirstName="Rosie",
                            LastName = "Cotton",
                            EmailAddress = "
rc@hob.me"}};
      return custList;
   }
}

In VB:

Public Class Customer

    Private _CustomerId As Integer
    Public Property CustomerId() As Integer
        Get
            Return _CustomerId
        End Get
        Set(ByVal value As Integer)
            _CustomerId = value
        End Set
    End Property

    Private _FirstName As String
    Public Property FirstName() As String
        Get
            Return _FirstName
        End Get
        Set(ByVal value As String)
            _FirstName = value
        End Set
    End Property

    Private _LastName As String   
    Public Property LastName() As String
        Get
            Return _LastName
        End Get
        Set(ByVal value As String)
            _LastName = value
        End Set
    End Property

    Private _EmailAddress As String
    Public Property EmailAddress () As String
        Get
            Return _EmailAddress
        End Get
        Set(ByVal value As String)
            _EmailAddress = value
        End Set
    End Property
End Class

Public Class Customers

    Public Shared Function Retrieve() As List(Of Customer)
        Dim custList As New List(Of Customer)
        custList.Add(New Customer With {.CustomerId = 1, _
                                        .LastName = "Baggins", _
                                        .FirstName = "Bilbo", _
                                        .EmailAddress = "bb@hob.me"})
        custList.Add(New Customer With {.CustomerId = 2, _
                                        .LastName = "Baggins", _
                                        .FirstName = "Frodo", _
                                        .EmailAddress = "fb@hob.me"})
        custList.Add(New Customer With {.CustomerId = 3, _
                                        .LastName = "Gamgee", _
                                        .FirstName = "Samwise", _
                                        .EmailAddress = "sg@hob.me"})
        custList.Add(New Customer With {.CustomerId = 4, _
                                        .LastName = "Cotton", _
                                        .FirstName = "Rosie", _
                                        .EmailAddress = "rc@hob.me"})
        Return custList
    End Function

End Class

The C# code here uses auto-implemented properties to shorten the property syntax. The VB code uses the full property syntax.

In a real application, the Retrieve method would collect the data from the database. This example uses hard-coded values to make it easier for you to try this code without having to set up data access.

NOTE: Since this example includes sorting and paging, you may want to add more test data to the lists to better see the sorting and paging in operation.

Define the GridView

The next step is to define the ASP.NET GridView control. This example creates the control using HTML, but you could create the control using code if desired.

In HTML:

<asp:GridView ID="CustomerGridView" runat="server"
    AllowPaging="true" PageSize="3"
    AllowSorting="true"
    AutoGenerateColumns="false">
    <Columns>
        <asp:BoundField HeaderText="Last Name"
            DataField="LastName" SortExpression="LastName" />
        <asp:BoundField HeaderText="First Name"
            DataField="FirstName" SortExpression="FirstName" />
        <asp:BoundField HeaderText="Email" 
            DataField="EmailAddress" SortExpression="EmailAddress" />
    </Columns>
</asp:GridView>

AllowPaging is set to true to demonstrate the paging feature. The PageSize is only set to 3 since this example includes such a small set of data. You can increase this number based on your user interface design.

AllowSorting is set to true to demonstrate the grid sorting feature. In addition, SortExpression was set for each BoundField.

AutoGenerateColumns is off so that the code can manually define the desired columns in the desired order.

Write the Code

Since there is no DataSource control in this example, you need to write code to perform the binding, sorting, and paging.

In this example, the Page_Load event calls the business object to obtain the data and performs the binding to that data.

ASP.NET generates the PageIndexChanging event when the grid is paging. So the code to handle the paging is in this event.

The Sorting event contains the code to handle the sorting. By default, the Sorting event will always request an ascending sort, so if you want your grid to sort both ascending and descending, you will need to handle that in the code. You can write the code so that when the user clicks on a column, the sort is ascending. If the user clicks on the same column again, the sort is descending. If the user clicks on a different column, the new column is sorted in ascending order.

In C#:

using System.Collections.Generic;
using System.Linq;
using System.Web.UI.WebControls;
using SampleBoCSharp;

namespace SampleWebCSharp
{
    public partial class _Default : System.Web.UI.Page
    {
        public string LastSortKey
        {
            get { return ViewState["LastSortKey"].ToString(); }
            set { ViewState["LastSortKey"] = value; }
        }

        public string LastSortDirection
        {
            get { return ViewState["LastSortDirection"].ToString(); }
            set { ViewState["LastSortDirection"] = value; }
        }

        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
            {
                // Get the list of customers
                List<Customer> customerList = Customers.Retrieve();

                // Do the binding
                CustomerGridView.DataSource = customerList;
                CustomerGridView.DataBind();

                // Store in a session variable
                Session.Add("Customers", customerList);

                // Set the sort info
                LastSortDirection = string.Empty;
                LastSortKey = string.Empty;
            }

            // Set up the events
            CustomerGridView.PageIndexChanging +=
                                   CustomerGridView_PageIndexChanging;
            CustomerGridView.Sorting += CustomerGridView_Sorting;
        }

        private void CustomerGridView_PageIndexChanging(object sender,
                                              GridViewPageEventArgs e)
        {
            // Get the list of customers from the session
            List<Customer> customerList = default(List<Customer>);
            customerList = Session["Customers"] as List<Customer>;
            // Set the index
            CustomerGridView.PageIndex = e.NewPageIndex;
            // Rebind
            CustomerGridView.DataSource = customerList;
            CustomerGridView.DataBind();
        }

        private void CustomerGridView_Sorting(object sender,
                                    GridViewSortEventArgs e)
        {
            // Get the list of customers from the session
            List<Customer> customerList = default(List<Customer>);
            customerList = Session["Customers"] as List<Customer>;
            // Sort key is different, clear the last sort direction
            if (LastSortKey != e.SortExpression) {
                LastSortDirection = string.Empty;
            }
            // Perform the sort using Linq
            switch (e.SortExpression) {
                case "LastName":
                    customerList = Sort(customerList,
                                        cust=> cust.LastName);
                    break;
                case "FirstName":
                    customerList = Sort(customerList,
                                        cust=> cust.FirstName);
                    break;
                case "EmailAddress":
                    customerList = Sort(customerList,
                                        cust => cust.EmailAddress);
                    break;
            }
            LastSortKey = e.SortExpression;
            // Rebind
            CustomerGridView.DataSource = customerList;
            CustomerGridView.DataBind();
            // Store in a session variable
            Session.Add("Customers", customerList);
        }

        private List<Customer> Sort(List<Customer> list,
                Func<Customer, string> sortKey)
        {
            if (LastSortDirection == "ASC") {
                list = list.OrderByDescending(sortKey).ToList();
                LastSortDirection = "DESC";
            }
            else {
                list = list.OrderBy(sortKey).ToList();
                LastSortDirection = "ASC";
            }
            return list;
        }
    }
}

In VB:

Partial Public Class _Default
    Inherits System.Web.UI.Page

    Public Property LastSortKey() As String
        Get
            Return ViewState("LastSortKey").ToString
        End Get
        Set(ByVal value As String)
            ViewState("LastSortKey") = value
        End Set
    End Property

    Public Property LastSortDirection() As String
        Get
            Return ViewState("LastSortDirection").ToString
        End Get
        Set(ByVal value As String)
            ViewState("LastSortDirection") = value
        End Set
    End Property

    Protected Sub Page_Load(ByVal sender As Object, _
             ByVal e As System.EventArgs) Handles Me.Load
        If Not IsPostBack Then
            ‘ Get the list of customers
            Dim customerList As List(Of Customer)
            customerList = Customers.Retrieve()

            ‘ Do the binding
            CustomerGridView.DataSource = customerList
            CustomerGridView.DataBind()

            ‘ Store in a session variable
            Session.Add("Customers", customerList)

            ‘ Set the sort info
            LastSortDirection = String.Empty
            LastSortKey = String.Empty
        End If
    End Sub

    Private Sub CustomerGridView_PageIndexChanging(ByVal sender As Object, ByVal e As GridViewPageEventArgs) Handles CustomerGridView.PageIndexChanging
        ‘ Get the list of customers from the session
        Dim customerList As List(Of Customer)
        customerList = TryCast(Session("Customers"),
                                 List(Of Customer))

        ‘ Set the index
        CustomerGridView.PageIndex = e.NewPageIndex

        ‘ Rebind
        CustomerGridView.DataSource = customerList
        CustomerGridView.DataBind()
    End Sub

    Private Sub CustomerGridView_Sorting(ByVal sender As Object, 
          ByVal e As GridViewSortEventArgs) 
          Handles CustomerGridView.Sorting
        ‘ Get the list of customers from the session
        Dim customerList As List(Of Customer)
        customerList = TryCast(Session("Customers"),
                                  List(Of Customer))

        ‘ If the sort key is different, clear the last sort direction
        If LastSortKey <> e.SortExpression Then
            LastSortDirection = String.Empty
        End If

        ‘ Perform the sort using Linq
        Select Case e.SortExpression
            Case "LastName"
                customerList = Sort(customerList,
                                 Function(cust) cust.LastName)

            Case "FirstName"
                customerList = Sort(customerList, 
                                 Function(cust) cust.FirstName)

            Case "EmailAddress"
                customerList = Sort(customerList, 
                                 Function(cust) cust.EmailAddress)

        End Select
        LastSortKey = e.SortExpression

        ‘ Rebind
        CustomerGridView.DataSource = customerList
        CustomerGridView.DataBind()

        ‘ Store in a session variable
        Session.Add("Customers", customerList)
    End Sub

   Private Function Sort(ByVal list As List(Of Customer), _
     ByVal sortKey As Func(Of Customer, String)) As List(Of Customer)

        If LastSortDirection = "ASC" Then
            list = list.OrderByDescending(sortKey).ToList()
            LastSortDirection = "DESC"
        Else
            list = list.OrderBy(sortKey).ToList()
            LastSortDirection = "ASC"
        End If
        Return list
    End Function
End Class

The LastSortKey and LastSortDirection properties retain the sort criteria so that you can sort ascending or descending. The  values are stored in the ViewState so they can be retained with the other page data.

The Page_Load event calls the Retrieve method on the Customers object to retrieve the list of customers. It then sets the DataSource property of  the GridView and binds it.

NOTE: For the paging to work correctly, the GridView must be bound to a List.

The code then stores the customer list in a session variable. This is not necessary if you want to ensure that the data is fresh each time that it is sorted or paged.

Finally, it sets a default value into the LastSortKey and LastSortDirection properties so they are not null.

The PageIndexChanged event retrieves the list of customers from the session variable, sets the GridView PageIndex, and rebinds to the list.

NOTE: If you re-retrieve the customer list instead of storing/retrieving it from the session, you will need to resort it before rebinding because it will have lost any sorting.

The Sorting event retrieves the list of customers from the session variable. It then checks the last sort key and clears the LastSortDirection if the user clicked on a different column. It then performs the sort using a lambda expression.

Finally, it rebinds the GridView to the list and stores the sorted list back to the session variable.

The Sort method defined in this example takes the list and a lambda expression in as parameters, performs the sort, and returns the sorted list.

The result looks like this:

image

You can then style the grid to match your user interface design. And adding an image to show whether the column was sorted ascending or descending would also be nice.

Enjoy!

8 Comments

  1.   Paul Keister — January 25, 2010 @ 12:26 pm    Reply

    This is a good overview of business object databinding, and it is important to understand that Asp.Net databinding relies on reflection, which is available for all CLR objects, and not on attributes.

    However, it should also be noted that changing POCOs to first class data objects is quite simple, all that is necessary is to add a few attributes. If one were to add the DataObject attribute to the customer class, the DataObjectMethod attribute to the Retrieve method, and DataObjectField attributes to each bindable property in the customer class. The property attributes can be a bit of a pain, I have a code snippet set up for this purpose. The point is that once the attributes are added, you can use the Add/Edit columns smart tag in the VS 2008 designer to manage your columns.

    In the long run, I have found using the data attributes save time, because they make layout tasks faster and eliminate the possiblity of spelling errors. But of course your results may vary. It is good to know about the alternative POCO approach if that works better for you.

  2.   Troopers — February 3, 2010 @ 7:12 am    Reply

    You can also bind the grid view on a ObjectDataSource where the Type is your custom object type and the methods select, update, insert, delete map on some methods of your custom class

  3.   Hemant — May 24, 2010 @ 8:38 am    Reply

    Hello ,

    I have a problem senario in which i have to srot decimal value column, with your provided solution its sorting for String only , i am new to Linq and lemda expression can you please help me in the same..

    Thanks,
    Hemant

  4.   sanil — June 8, 2010 @ 5:10 pm    Reply

    Hi,
    I am pretty new to asp.net.
    If in your example, the customer class has a property which is equal to another Object, then such a property is not displayed in asp.net datagrid. why and what needs to be done?
    It appears in winforms without any trouble.

    Public class Customer


    public property My_SalesPerson as SalesPerson
    get
    set
    End Property

    Even if the SalesPerson class has a overridden toString method, the value does not appear on the grid. Only primitive datatypes are displayed.

  5.   Tim — July 21, 2010 @ 6:16 pm    Reply

    It would be nice to be able to bind a generic list to a custom gridview so that every time this gridview is implemented, those methods don’t have to be reimplemented.. Just seems like a waste of time and code to write the same paging and sort routines every time a gridview is used.

  6.   Matt — July 28, 2010 @ 5:39 am    Reply

    Very helpful post.

    Thanks

    Matt

  7.   Adil — March 18, 2011 @ 2:40 am    Reply

    Thank you very much…u saved me

  8.   Carlos — July 11, 2013 @ 4:28 am    Reply

    Thank you very much.
    i have Q.
    it can be sort?

RSS feed for comments on this post. TrackBack URI

Leave a comment

© 2014 Deborah's Developer MindScape   Provided by WPMU DEV -The WordPress Experts   Hosted by Microsoft MVPs