Blog Archives
asp .net grid control common gridview operations update edit cancel sort paging
There are many ways to accomplish the end result of what is displayed below.
To display multiple records of data on the same page, you may alternatively use Repeater (my favorite of the dataview controls), DataList or ListView (also great).
FormView and DetailsView are similar, but should be limited to smaller sets of data IMO or in a system where limited results are only displayed in this control after selection from a search or query filter.
The control on the HTML design side:
<asp:UpdatePanel ID="upnlContent" runat="server" UpdateMode="Conditional" ChildrenAsTriggers="false"> <ContentTemplate> <div id="modal" style="display: none;" title="Application Message"> <asp:Label ID="lblError" runat="server"></asp:Label> </div> <asp:GridView ID="gvDataList" runat="server" AllowPaging="True" OnRowEditing="gvDataList_EditRecord" OnRowCancelingEdit="gvDataList_CancelRecord" OnRowUpdating="gvDataList_UpdateRecord" AutoGenerateColumns="False" AllowSorting="true" PageSize="50" DataKeyNames="TreeID" OnRowCommand="gvDataList_RowCommand" OnSorting="gvDataList_Sorting" OnPageIndexChanging="gvDataList_PageIndexChanging"> <AlternatingRowStyle BackColor="#EFEFEF" /> <Columns> <asp:BoundField DataField="TreeID" HeaderText="TreeID" InsertVisible="False" SortExpression="TreeID" ReadOnly="True" /> <asp:TemplateField HeaderText="ActiveStatus" SortExpression="ActiveStatus"> <ItemTemplate> <asp:Label ID="lblActiveStatus" runat="server" Text='<%# Eval("ActiveStatus")%>'></asp:Label> </ItemTemplate> <EditItemTemplate> <asp:CheckBox ID="chkActiveStatus" runat="server" Checked='<%# Convert.ToBoolean(Eval("ActiveStatus")) %>' Enabled='<%# GetEditPermission("activestatus") %>' /> </EditItemTemplate> </asp:TemplateField> <asp:TemplateField HeaderText="Name" SortExpression="Name"> <ItemTemplate> <asp:HyperLink ID="Name" runat="server" NavigateUrl='<%# "~/Pages/ExistingOrange.aspx?TreeID=" + Eval("TreeID")%>'> <%# Eval("Name")%> </asp:HyperLink> </ItemTemplate> <EditItemTemplate> <asp:TextBox ID="tbtreename" runat="server" Text='<%# Eval("Name") %>' /> </EditItemTemplate> </asp:TemplateField> <asp:TemplateField HeaderText="Description" SortExpression="Description"> <ItemTemplate> <asp:HyperLink ID="hltreedescription" runat="server" NavigateUrl='<%# "~/Pages/ExistingOrange.aspx?TreeID=" + Eval("TreeID")%>'> <%# Eval("Description")%> </asp:HyperLink> </ItemTemplate> <EditItemTemplate> <asp:TextBox ID="tbtreedescription" runat="server" Text='<%# Eval("Description") %>' /> </EditItemTemplate> </asp:TemplateField> <asp:TemplateField HeaderText="Country" SortExpression="CountryID" ItemStyle-HorizontalAlign="Center"> <ItemTemplate> <asp:Label ID="lblCountryID" runat="server" Text='<%# getcountryname(Eval("CountryID"))%>'></asp:Label> </ItemTemplate> <EditItemTemplate> <asp:DropDownList ID="ddlCountryID" runat="server" Enabled='<%# GetEditPermission("allowregion") %>' DataSource='<%# countriesdropdown %>' DataTextField='Description' DataValueField="CountryID" /> </EditItemTemplate> </asp:TemplateField> <asp:BoundField DataField="LastUpdated" HeaderText="Last Updated" SortExpression="LastUpdated" ReadOnly="true" /> <asp:BoundField DataField="ExpirationDate" HeaderText="Expiration Date" InsertVisible="False" SortExpression="ExpirationDate" ReadOnly="True" /> <asp:BoundField DataField="runcount" HeaderText="Pick Count" InsertVisible="False" SortExpression="PickCount" ReadOnly="True" /> <asp:CommandField ButtonType="Button" EditText="Edit" UpdateText="Update" CancelText="Cancel" HeaderText="Edit Row" ShowEditButton="true" /> <asp:ButtonField CommandName="NewOrange" ButtonType="Button" Text="Create" HeaderText="New Orange" /> </Columns> </asp:GridView> </ContentTemplate> </asp:UpdatePanel>
The Events and functions attached to the control on the code behind side:
private void LoadData(string sort, bool asc) { DAL.AC_DataClassesDataContext dc = new DAL.AC_DataClassesDataContext(); var data = (from Trees in dc.Trees join Oranges in dc.Oranges on new { TreeID = Trees.TreeID } equals new { TreeID = Convert.ToInt32(Oranges.TreeID) } into Oranges_join from Oranges in Oranges_join.DefaultIfEmpty() group new { Oranges, Trees } by new { Trees.TreeID, Trees.ActiveStatus, Trees.Name, Trees.Description, Trees.CountryID, Trees.LastUpdated } into g select new { TreeID = (System.Int32?)g.Key.TreeID, ExpirationDate = (System.DateTime?)g.Max(p => p.Oranges.PickDate), PickCount = g.Count(), ActiveStatus = (System.Byte?)g.Key.ActiveStatus, g.Key.Name, g.Key.Description, g.Key.CountryID, LastUpdated = (System.DateTime?)g.Key.LastUpdated }); data = data.OrderByField(sort, asc); //OrderByField uses dynamic linq library which is a free additional open source download from MS try { data = data.Where(g => g.TreeID == Convert.ToInt32(ddlTreeIDs.SelectedValue)); } catch (Exception ex) { lblError.Text = ex.ToString(); utils.ShowJQueryModal(this, ex); //static custom class I created to show dialog containing lblerror text } gvDataList.DataSource = data; gvDataList.DataBind(); upnlContent.Update(); } protected void gvDataList_RowCommand(object sender, GridViewCommandEventArgs e) { if (e.CommandName == "NewOrange") { string redirecturl = "~/Pages/NewOrange.aspx?TreeID={0}"; //this page should be pretty self explanatory string TreeID = (((GridView)sender).Rows[Convert.ToInt32(e.CommandArgument)].Cells[0]).Text; redirecturl = String.Format(redirecturl, TreeID); Response.Redirect(redirecturl); } } //example of a mechanism that can be used to change style or visibility of controls specific to user access public bool GetEditPermission(string permtype) { switch (permtype.ToLower()) { case "other": if (utils.usergroup == "admin") { return true; } else return false; case "allowregion": if (utils.usergroup == "admin") { return true; } else return false; default: return true; } } protected void gvDataList_UpdateRecord(object sender, GridViewUpdateEventArgs e) { DAL.AC_DataClassesDataContext dc = new DAL.AC_DataClassesDataContext(); int TreeID, activestatus, CountryID; string Name, Description; try { TreeID = Convert.ToInt32(e.Keys[0]); //should be 0 activestatus = Convert.ToInt32(((CheckBox)gvDataList.Rows[e.RowIndex].FindControl("chkActiveStatus")).Checked); Name = Convert.ToString(((TextBox)gvDataList.Rows[e.RowIndex].FindControl("tbName")).Text); Description = Convert.ToString(((TextBox)gvDataList.Rows[e.RowIndex].FindControl("tbDescription")).Text); CountryID = Convert.ToInt32(((DropDownList)gvDataList.Rows[e.RowIndex].FindControl("ddlCountryID")).SelectedValue); } catch (Exception ex) { //throw new Exception("Error retrieving grid values for update."); lblError.Text = "Error retrieving grid values for update. Details: " + ex.ToString(); utils.ShowJQueryModal(this); upnlContent.Update(); return; //unassigned local variables if this is skipped or exception now thrown } int result = dc.ExecuteCommand("update Trees set ActiveStatus={1}," + "Name={2},Description={3}," + "CountryID={4} where TreeID={0}", TreeID, activestatus, Name, Description, CountryID); if (result == 1) { } else { lblError.Text = "Record failed to update."; utils.ShowJQueryModal(this); //static custom class I created to show modal dialog containing lblerror text } gvDataList.EditIndex = -1; LoadData(); } protected void gvDataList_EditRecord(object sender, GridViewEditEventArgs e) { gvDataList.EditIndex = e.NewEditIndex; LoadData(); } protected void gvDataList_CancelRecord(object sender, GridViewCancelEditEventArgs e) { gvDataList.EditIndex = -1; LoadData(); } private string ConvertSortDirectionToSql(SortDirection sortDirection) { string newSortDirection = String.Empty; switch (sortDirection) { case SortDirection.Ascending: newSortDirection = "ASC"; break; case SortDirection.Descending: newSortDirection = "DESC"; break; } return newSortDirection; } protected void gvDataList_PageIndexChanging(object sender, GridViewPageEventArgs e) { gvDataList.PageIndex = e.NewPageIndex; gvDataList.DataBind(); } protected void gvDataList_Sorting(object sender, GridViewSortEventArgs e) { bool asc = true; //default to true per usual .net behavior if (ViewState[e.SortExpression] != null) { asc = (!(bool)ViewState[e.SortExpression]); ViewState[e.SortExpression] = asc; } else { ViewState[e.SortExpression] = true; } LoadData(e.SortExpression, asc); } //Show modal dialog using jquery. Requires javascript reference/link to jquery in the page. public static void ShowJQueryModal(Page PageInstance, Exception ex) { ClientScriptManager ClientScript = PageInstance.ClientScript; AjaxControlToolkit.ToolkitScriptManager.RegisterStartupScript(PageInstance, PageInstance.GetType(), "preparemodal", "<script type=\"text/javascript\">$('#modal').dialog({autoOpen: false, modal: true });</script>", false); AjaxControlToolkit.ToolkitScriptManager.RegisterStartupScript(PageInstance, PageInstance.GetType(), "loadmodal", "<script type=\"text/javascript\">$('#modal').dialog('open');</script>", false); }
Links and references to Dynamic LINQ may change over time (no pun intended).
At the time of writing this they can be found on MS and Scott Gu’s blog. If you cannot find it, send me an email and I can send it to you or you can refer to my post on OrderBy Column Name for the exact extension method.
OrderBy Column Name as String using Linq C# .Net Dynamic Sorting of Anonymous Types
If you’re familiar with Linq, you have undoubtedly used the popular “OrderBy” extension method. Unfortunately, this method does not accept a string value with the column name.
var data = from i in db.tablename select i; repeatername.datasource = data.OrderBy(i=>i.columnname); //this works repeatername.datasource = data.OrderBy("columnname"); //this does not
To resolve, you can add the following small class or just the method to a generic DLL you use in your web application projects or create a new class or project for your extension methods.
Kudos to R. Prestol for help researching this one.
using System.Linq; using System.Linq.Expressions; using System; namespace YourAppName.Web { public static class extensionmethods { public static IQueryable<T> OrderByField<T>(this IQueryable<T> q, string SortField, bool Ascending) { var param = Expression.Parameter(typeof(T), "p"); var prop = Expression.Property(param, SortField); var exp = Expression.Lambda(prop, param); string method = Ascending ? "OrderBy" : "OrderByDescending"; Type[] types = new Type[] { q.ElementType, exp.Body.Type }; var mce = Expression.Call(typeof(Queryable), method, types, q.Expression, exp); return q.Provider.CreateQuery<T>(mce); } } }
Returning to our first code snippet, you can now do:
repeater.datasource = data.OrderByField("columnname");
Since this is an extension method, and exists in a class within the same namespace as your project or other referenced assembly, you can call the method directly from the IQueryable Linq data object without requiring any other inherits; this works similar to override functionality.
Read up about extension methods on MSDN or Google for other cool tricks you can do. Enjoy. 😉
References
Extension Methods (MSDN), http://msdn.microsoft.com/en-us/library/bb383977.aspx