static void

Basic GridView

Binding an plain old CLR object list (or a DataTable, in comments) to a GridView with paging, sorting and editing.

The page

For paging and sorting only a plain gridview with BoundFields or autogenerated columns works fine- but for editing you need templated columns

<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False">
    <Columns>
        <asp:TemplateField HeaderText="Name" SortExpression="Name">
            <EditItemTemplate>
                <asp:TextBox ID="txtName" runat="server" Text='<%# Bind("Name") %>' />
            </EditItemTemplate>
            <ItemTemplate>
                <asp:Label ID="Label1" runat="server" Text='<%# Bind("Name") %>' />
            </ItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="Date" SortExpression="Date">
            <EditItemTemplate>
                <asp:TextBox ID="txtDate" runat="server" Text='<%# Bind("Date", "{0:d}") %>' />
                <asp:CompareValidator ID="cvdate" runat="server" ControlToValidate="txtDate" ErrorMessage="Must be a date"
                    Operator="DataTypeCheck" Type="Date" />
            </EditItemTemplate>
            <ItemTemplate>
                <asp:Label ID="Label2" runat="server" Text='<%# Bind("Date", "{0:d}") %>' />
            </ItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField>
            <ItemTemplate>
                <asp:Button runat="server" ID="btnClick" Text="Date to Today" CommandName="SetToday" CommandArgument='<%# ((GridViewRow) Container).RowIndex %>'/>
            </ItemTemplate>
        </asp:TemplateField>
    </Columns>
</asp:GridView>

The code behind:

using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Web.UI;
using System.Web.UI.WebControls;
 
public partial class TestGrid : Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        AttachEvents();
 
        if (!IsPostBack)
        {
            StoredData = LoadDatedItems();
            SetupGridView();
            GridView1.DataSource = StoredData;
            GridView1.DataBind();
        }
    }
 
    /// <summary>
    /// Initialize the grid view or put these as attributes
    /// </summary>
    private void SetupGridView()
    {
        AllowSortingAndPaging(GridView1);
 
        //for editing
        GridView1.DataKeyNames = new string[] { "Id" }; //customize this
        GridView1.AutoGenerateSelectButton = true;
        GridView1.AutoGenerateEditButton = true;
 
    }
 
    private static void AllowSortingAndPaging(GridView gv)
    {
        gv.AllowPaging = true;
        gv.AllowSorting = true;
        gv.PageSize = 20; //if you edit and turn off paging, this gets killed
    }
 
    /// <summary>
    /// Bind events every postback, or put these as attributes
    /// </summary>
    private void AttachEvents()
    {
        GridView1.Sorting += GridView1_Sorting;
        GridView1.PageIndexChanging += GridView1_PageIndexChanging;
 
        //for editing
        GridView1.RowCommand += GridView1_RowCommand;
        GridView1.RowEditing += GridView1_RowEditing;
        GridView1.RowUpdating += GridView1_RowUpdating;
        GridView1.RowCancelingEdit += GridView1_RowCancelingEdit;
    }
 
    #region GridView events
 
    private void GridView1_PageIndexChanging(object sender, GridViewPageEventArgs e)
    {
        GridView gv = (GridView)sender;
        gv.PageIndex = e.NewPageIndex; //simply set the page
        GridView1.DataSource = StoredData; //rebind to stored data
        gv.DataBind();
    }
 
    private void GridView1_Sorting(object sender, GridViewSortEventArgs e)
    {
        GridView gv = (GridView)sender;
        gv.PageIndex = 0; //always sort to page 1
 
        GetSortDirection(e.SortExpression); //sort direction is always ascending, so we do this manually
        GridViewSortBy = e.SortExpression; //sortBy is stored so we can change direction next time
        SortData(); //for a poco, you must hardcode the mapping to the properties
 
        GridView1.DataSource = StoredData; //rebind to stored data
        gv.DataBind();
    }
 
    private void GetSortDirection(string sortBy)
    {
        //swap the sort direction if sorting same column
        if (GridViewSortBy != sortBy)
            GridViewSortAscending = true;
        else
            GridViewSortAscending = !GridViewSortAscending;
    }
 
    private void GridView1_RowCommand(object sender, GridViewCommandEventArgs e)
    {
        GridView gv = (GridView)sender;
 
        //NB this is also fired by Paging, Sorting, Editing etc...
        if (e.CommandName.Equals("Select", StringComparison.OrdinalIgnoreCase))
            SelectCommand(e.CommandArgument, gv);
        if (e.CommandName.Equals("SetToday", StringComparison.OrdinalIgnoreCase))
            TodayCommand(e.CommandArgument, gv);
    }
 
 
    private static int GetKeyFromDataKeys(GridView gv, int rowIndex)
    {
        //assumes key is an integer first in DataKeys. Adjust for strings or multiple IDs.
        if (rowIndex <= gv.DataKeys.Count)
            return (int)gv.DataKeys[rowIndex][0];
        return -1; //might throw an error here
    }
 
    private static void SelectCommand(object commandArgument, GridView gv)
    {
        //a templated field may shortcircuit with the key (Eval("Id")) but you won't get the row
        int rowIndex = Convert.ToInt32(commandArgument);
        GridViewRow row = gv.Rows[rowIndex];
        //int key = GetKeyFromDataKeys(gv, rowIndex); //if needed
        row.BackColor = Color.AliceBlue;
    }
 
    private void TodayCommand(object commandArgument, GridView gv)
    {
        int rowIndex = Convert.ToInt32(commandArgument);
        //GridViewRow row = gv.Rows[rowIndex];
        int key = GetKeyFromDataKeys(gv, rowIndex);
        SetDateToToday(key);
 
        gv.DataSource = StoredData; //rebind
        gv.DataBind();
    }
 
    private void GridView1_RowEditing(object sender, GridViewEditEventArgs e)
    {
        GridView gv = (GridView)sender;
        //to get index and row
        //int rowIndex = e.NewEditIndex; //here we have the row directly
        //GridViewRow row = gv.Rows[rowIndex];
 
        gv.EditIndex = e.NewEditIndex;
        //you must rebind
        gv.DataSource = StoredData;
        gv.DataBind();
        //important - turn off paging and sorting
        //(do this after binding, unlike in updating and canceledit)
        gv.AllowPaging = false;
        gv.AllowSorting = false;
    }
 
    private void GridView1_RowCancelingEdit(object sender, GridViewCancelEditEventArgs e)
    {
        GridView gv = (GridView)sender;
        gv.EditIndex = -1;
        //turn back on paging and sorting
        AllowSortingAndPaging(GridView1);
        //you must rebind last of all
        gv.DataSource = StoredData;
        gv.DataBind();
    }
 
    private void GridView1_RowUpdating(object sender, GridViewUpdateEventArgs e)
    {
        GridView gv = (GridView)sender;
        //to get index and row
        int rowIndex = e.RowIndex; //here we have the row directly
        GridViewRow row = gv.Rows[rowIndex]; //the row
        GetDataFromEventArgs(e); //this won't work here (for reference only)
        GetDataFromTemplatedRow(gv, rowIndex, row); //use FindControl
 
        gv.EditIndex = -1;
        //turn back on paging and sorting
        AllowSortingAndPaging(GridView1);
        //you must rebind last of all
        gv.DataSource = StoredData;
        gv.DataBind();
    }
 
    private static string GetTextBoxText(Control row, string Id)
    { //helper to get the text from a textbox in a gridrow
        TextBox txtName = row.FindControl(Id) as TextBox;
        if (txtName != null) return txtName.Text;
        else return String.Empty;
    }
    #endregion
 
    #region Properties storing GridView data and settings
 
    private bool GridViewSortAscending
    {
        get { return (ViewState["SortDirection"] == null) ? true : (bool)ViewState["SortDirection"]; }
        set { ViewState["SortDirection"] = value; }
    }
 
    private string GridViewSortBy
    {
        get { return ViewState["SortBy"] as string ?? ""; }
        set { ViewState["SortBy"] = value; }
    }
 
    #endregion
 
 
    #region Alter to fit data type
    /// <summary>
    /// Generates some dummy data
    /// </summary>
    private static List<DatedItem> LoadDatedItems()
    {
        List<DatedItem> items = new List<DatedItem>();
        for (int i = 0; i < 30; i++)
        {
            DatedItem di = new DatedItem();
            di.Date = DateTime.Now.AddDays(-i);
            di.Id = i;
            di.Name = di.Date.ToString("MMMM dd");
            items.Add(di);
        }
        return items;
    }
 
    /// <summary>
    ///  Sorts the data manually
    /// </summary>
    private void SortData()
    {
        if (GridViewSortBy.Equals("Name", StringComparison.OrdinalIgnoreCase))
        {
            if (GridViewSortAscending)
                StoredData.Sort(delegate(DatedItem p1, DatedItem p2)
                { return p1.Name.CompareTo(p2.Name); });
            else
                StoredData.Sort(delegate(DatedItem p1, DatedItem p2)
                { return -p1.Name.CompareTo(p2.Name); });
        }
        else if (GridViewSortBy.Equals("Id", StringComparison.OrdinalIgnoreCase))
        {
            if (GridViewSortAscending)
                StoredData.Sort(delegate(DatedItem p1, DatedItem p2)
                { return p1.Id.CompareTo(p2.Id); });
            else
                StoredData.Sort(delegate(DatedItem p1, DatedItem p2)
                { return -p1.Id.CompareTo(p2.Id); });
        }
        else if (GridViewSortBy.Equals("Date", StringComparison.OrdinalIgnoreCase))
        {
            if (GridViewSortAscending)
                StoredData.Sort(delegate(DatedItem p1, DatedItem p2)
                { return p1.Date.CompareTo(p2.Date); });
            else
                StoredData.Sort(delegate(DatedItem p1, DatedItem p2)
                { return -p1.Date.CompareTo(p2.Date); });
        }
    }
 
    private List<DatedItem> StoredData
    {
        get { return ViewState["StoredData"] as List<DatedItem> ?? LoadDatedItems(); }
        set { ViewState["StoredData"] = value; }
    }
 
    private void GetDataFromTemplatedRow(GridView gv, int rowIndex, Control row)
    {
        //you MUST use templated columns (you should anyway to validate), so use FindControl
        string name = GetTextBoxText(row, "txtName");
        string date = GetTextBoxText(row, "txtDate");
        int key = (int)gv.DataKeys[rowIndex][0];
        UpdateData(key, name, date);
    }
 
    private void GetDataFromEventArgs(GridViewUpdateEventArgs e)
    {
        //autogenerated columns tied to an object (not a datasource) don't work here.
        if (e.Keys.Count > 0)
        {
            int key = (int)e.Keys[0]; //if using DataKeys
            DatedItem di = StoredData.Find(delegate(DatedItem d) { return (d.Id == key); });
            if (di != null)
                di.Name = (string)e.NewValues["Name"];
        }
    }
 
    private void UpdateData(int key, string name, string date)
    {
        DatedItem di = StoredData.Find(delegate(DatedItem d) { return (d.Id == key); });
        if (di == null) return;
 
        di.Name = name;
        di.Date = Convert.ToDateTime(date);
    }
 
 
    private void SetDateToToday(int key)
    {
        List<DatedItem> copy = StoredData;
        int index = copy.FindIndex(delegate(DatedItem d) { return (d.Id == key); });
        if (index != -1)
            copy[index].Date = DateTime.Now;
        StoredData = copy;
    }
 
    #region For reference- same code for DataTable
    //private static DataTable LoadDatedItems()
    //{
    //    List<DatedItem> items = new List<DatedItem>();
    //    for (int i = 0; i < 30; i++)
    //    {
    //        DatedItem di = new DatedItem();
    //        di.Date = DateTime.Now.AddDays(-i);
    //        di.Id = i;
    //        di.Name = di.Date.ToString("MMMM dd");
    //        items.Add(di);
    //    }
    //    DataTable dt = new DataTable("DatedItems");
    //    dt.Columns.Add("name"); dt.Columns.Add("id", typeof(int)); dt.Columns.Add("date", typeof(DateTime));
    //    foreach (DatedItem item in items)
    //        dt.Rows.Add(item.Name, item.Id, item.Date);
    //    return dt;
    //}
 
    //private void SortData()
    //{
    //    StoredData.DefaultView.Sort = GridViewSortBy + (GridViewSortAscending ? " ASC" : " DESC");
    //}
 
    //private DataTable StoredData
    //{
    //    get { return ViewState["StoredData"] as DataTable ?? LoadDatedItems(); }
    //    set { ViewState["StoredData"] = value; }
    //}
 
    //private void GetDataFromTemplatedRow(GridView gv, int rowIndex, Control row)
    //{   //you MUST use templated columns (you should anyway to validate), so use FindControl
    //    string name = GetTextBoxText(row, "txtName");
    //    string date = GetTextBoxText(row, "txtDate");
    //    int key = (int)gv.DataKeys[rowIndex][0];
    //    DataRow datarow = FindDataRow(key);
    //    if (datarow != null)
    //    {
    //        datarow["Name"] = name;
    //        datarow["Date"] = date;
    //    }
    //}
 
    //private DataRow FindDataRow(int key)
    //{
    //    DataRow[] rows = StoredData.Select("[id]='" + key + "'");
    //    if (rows.Length != 0) return rows[0];
    //    else return null;
    //}
 
    //private void GetDataFromEventArgs(GridViewUpdateEventArgs e)
    //{
    //    if (e.Keys.Count > 0)
    //    {
    //        int key = (int)e.Keys[0]; //if using DataKeys
    //        DataRow datarow = FindDataRow(key);
    //        if (datarow != null)
    //            datarow["Name"] = e.NewValues["Name"];
    //    }
    //}
 
    //private void SetDateToToday(int key)
    //{
    //    DataRow row= FindDataRow(key);
    //    row["Date"] = DateTime.Now;
    //}
    #endregion
 
    #endregion
 
 
}
 
//poco must be serializable so we can store it
[Serializable]
public class DatedItem
{
    private DateTime _date;
    private int _id;
    private string _name;
 
    public DateTime Date
    {
        get { return _date; }
        set { _date = value; }
    }
 
    public int Id
    {
        get { return _id; }
        set { _id = value; }
    }
 
    public string Name
    {
        get { return _name; }
        set { _name = value; }
    }
}