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; }
}
}