GridView Edit & Update Demo

C# Website WebForms GridView DataTables
GridView Edit & Update Demo

There are still a lot of qestions being asked on Stack Overflow about the Webforms GridView and how to edit or update data.
I always linked them to an online example that showed it all. But the site is offline so time to create my own demo. It also shows how to use the GridView sorting function.

This demo show how to bind data to a Strongly Typed GridView and then Sort, Edit, Update or Delete data items withing the GridView.
This demo also implements jQuery Datatable and UpdatePanel so you can see how to rebind DOM items after a Partial PostBack.

View source on GitHub

The Editable GridView

Code Snippets

This demo uses two Book classes for the test data. The ID of the class is imortant. That will be used to identify the correct object in the GridView so you edit the correct Book.

public class Book
{
    public int ID { get; set; }
    public string Title { get; set; }
    public BookCategory Category { get; set; }
    public DateTime Date { get; set; }

    public string CategoryName
    {
        get
        {
            return Category != null ? Category.Name : null;
        }
    }
}

public class BookCategory
{
    public int ID { get; set; }
    public string Name { get; set; }
}

The important GridView properties to make it function correctly. The rest like "CssClass" etc. is basic stuff that I won't explain here.

EmptyDataText="No books found."
This text will be shown when there is data bound to the GridView but the collection was NULL or empty.

EmptyDataRowStyle-CssClass="fw-bold"
The class for the row with the EmptyDataText.

DataKeyNames="ID"
Set the name of your class Indentifier here as DataKeyNames. In this demo that is "ID".

ItemType="GridViewEditDemo.Classes.Books.Book"
To make a GridView Strongly Typed set the full namespace to your class. Then you can access it's properties directly in the aspx with "Item": <%# Item.Name %>

OnRowDataBound="GridView_RowDataBound"
This triggers when a row is added to the GridView. You need this to create Datatable or set values of Controls in the row that is being edited.

OnRowEditing="GridView_RowEditing"
Set the GridView in edit mode with the row clicked to be edited.

OnRowCancelingEdit="GridView_RowCancelingEdit"
Cancels edit mode and returns the GridView to normal.

OnRowUpdating="GridView_RowUpdating"
Triggered when you click the "Update" button in edit mode. This will save the changes and return the GridView to normal.

OnRowDeleting="GridView_RowDeleting"
Triggered when you click the "Delete" button.

OnSorting
Triggered by clicking the links in the Header row.


The complete aspx code.

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="GridViewEditDemo.Default" %>

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>

    <title>VDWWD - GridView Edit & Update Demo</title>

    <!-- style sheets -->

    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" />
    <link rel="stylesheet" href="https://cdn.datatables.net/1.10.16/css/jquery.dataTables.min.css" />
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.7.0/css/bootstrap-datepicker.min.css" />

    <style>
        body {
            background-color: #4b4d66;
        }

        /* to make the tables slightly more compact */
        .btn {
            padding: 3px 10px;
        }

        .table th,
        .table td,
        table.dataTable thead th,
        table.dataTable tbody td {
            padding: 4px;
            vertical-align: middle;
        }

        .table th {
            background-color: #adadad;
            border-top: 0px;
            border-bottom: 1px solid #808080 !important;
        }

            .table th a {
                color: black;
                /* by making the header link a block you can click the entire cell */
                display: block;
            }
    </style>

</head>
<body>

    <!-- van der Waal Webdesign -->
    <!-- https://www.vanderwaal.eu -->

    <form id="form1" runat="server">

        <!-- When using ScriptManager with target framework 4.8 or higher, the validators on the page will NOT prevent a PostBack... -->

        <asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>


        <!-- Used an updatepanel because many people are having problems making something like datepickers, datatables, masonry, autocomplete jqery functions work after Partial PostBack -->

        <asp:UpdatePanel ID="UpdatePanel1" runat="server">
            <ContentTemplate>

                <div class="container">

                    <div class="row">
                        <div class="col p-3">

                            <!-- This GridView is editable -->

                            <asp:GridView ID="GridView1"
                                runat="server"
                                CssClass="table table-striped"
                                BackColor="White"
                                AutoGenerateColumns="false"
                                EmptyDataText="No books found."
                                EmptyDataRowStyle-CssClass="font-weight-bold"
                                DataKeyNames="ID"
                                AllowSorting="True"
                                ItemType="GridViewEditDemo.GridViewDemo.Book"
                                OnRowDataBound="GridView_RowDataBound"
                                OnRowEditing="GridView_RowEditing"
                                OnRowCancelingEdit="GridView_RowCancelingEdit"
                                OnRowUpdating="GridView_RowUpdating"
                                OnRowDeleting="GridView_RowDeleting"
                                OnSorting="GridView_Sorting">
                                <Columns>
                                    <asp:TemplateField HeaderText="ID">
                                        <HeaderTemplate>
                                            <asp:LinkButton ID="LinkButton1" runat="server" Text="ID" CommandName="Sort" CommandArgument="ID"></asp:LinkButton>
                                        </HeaderTemplate>
                                        <ItemTemplate>

                                            <!-- You can use "Item" to access properties because the GridView is Strongly Typed (set the ItemType of the GridView) -->

                                            <%# Item.ID %>
                                        </ItemTemplate>
                                    </asp:TemplateField>
                                    <asp:TemplateField HeaderText="Title">
                                        <HeaderTemplate>
                                            <asp:LinkButton ID="LinkButton2" runat="server" Text="Title" CommandName="Sort" CommandArgument="Title"></asp:LinkButton>
                                        </HeaderTemplate>
                                        <ItemTemplate>

                                            <%# Item.Title %>
                                        </ItemTemplate>
                                        <EditItemTemplate>

                                            <asp:TextBox ID="TextBox1" runat="server" MaxLength="50" CssClass="form-control form-control-sm"></asp:TextBox>

                                            <!-- Make a ValidationGroup per editable GridView. Make sure the "Update" button also has it -->

                                            <asp:RequiredFieldValidator ID="RequiredFieldValidator1" runat="server" ValidationGroup="EditGridView" Display="Dynamic"
                                                ControlToValidate="TextBox1" CssClass="text-danger font-weight-bold" ErrorMessage="Title is required.">
                                            </asp:RequiredFieldValidator>

                                        </EditItemTemplate>
                                    </asp:TemplateField>
                                    <asp:TemplateField HeaderText="Category">
                                        <HeaderTemplate>
                                            <asp:LinkButton ID="LinkButton3" runat="server" Text="Category" CommandName="Sort" CommandArgument="Category"></asp:LinkButton>
                                        </HeaderTemplate>
                                        <ItemTemplate>

                                            <%# Item.Category.Name %>
                                        </ItemTemplate>
                                        <EditItemTemplate>

                                            <asp:DropDownList ID="DropDownList1" runat="server" AppendDataBoundItems="true" CssClass="form-control form-control-sm">
                                                <asp:ListItem Text="Select..." Value=""></asp:ListItem>
                                            </asp:DropDownList>

                                            <asp:RequiredFieldValidator ID="RequiredFieldValidator2" runat="server" ValidationGroup="EditGridView" Display="Dynamic"
                                                ControlToValidate="DropDownList1" CssClass="text-danger font-weight-bold" ErrorMessage="Category is required.">
                                            </asp:RequiredFieldValidator>

                                        </EditItemTemplate>
                                    </asp:TemplateField>
                                    <asp:TemplateField HeaderText="Date">
                                        <HeaderTemplate>
                                            <asp:LinkButton ID="LinkButton4" runat="server" Text="Date" CommandName="Sort" CommandArgument="Date"></asp:LinkButton>
                                        </HeaderTemplate>
                                        <ItemTemplate>

                                            <%# Item.Date.ToLongDateString() %>
                                        </ItemTemplate>
                                        <EditItemTemplate>

                                            <asp:TextBox ID="TextBox2" runat="server" MaxLength="10" CssClass="form-control form-control-sm bootstrap-datepicker" placeholder="dd/mm/yyyy"></asp:TextBox>

                                            <asp:RequiredFieldValidator ID="RequiredFieldValidator3" runat="server" ValidationGroup="EditGridView" Display="Dynamic"
                                                ControlToValidate="TextBox2" CssClass="text-danger font-weight-bold" ErrorMessage="Date is required.">
                                            </asp:RequiredFieldValidator>

                                        </EditItemTemplate>
                                    </asp:TemplateField>
                                    <asp:TemplateField HeaderText="" HeaderStyle-Width="30">
                                        <ItemTemplate>

                                            <!-- do not change the CommandName -->

                                            <asp:LinkButton runat="server" CommandName="Edit" CssClass="btn btn-primary">Edit</asp:LinkButton>

                                        </ItemTemplate>
                                        <EditItemTemplate>

                                            <!-- do not change the CommandName -->

                                            <asp:LinkButton runat="server" CommandName="Update" CssClass="btn btn-success" ValidationGroup="EditGridView">Update</asp:LinkButton>

                                        </EditItemTemplate>
                                    </asp:TemplateField>

                                    <asp:TemplateField HeaderText="" HeaderStyle-Width="30">
                                        <ItemTemplate>

                                            <!-- do not change the CommandName -->

                                            <asp:LinkButton runat="server" CommandName="Delete" CssClass="btn btn-danger"
                                                OnClientClick='<%# "return confirm(\u0027Are you sure you want to delete the book \"" + Item.Title + "\"?\u0027)" %>'>
                                                        Delete
                                            </asp:LinkButton>

                                        </ItemTemplate>
                                        <EditItemTemplate>

                                            <!-- do not change the CommandName -->

                                            <asp:LinkButton runat="server" CommandName="Cancel" CssClass="btn btn-secondary">Cancel</asp:LinkButton>

                                        </EditItemTemplate>
                                    </asp:TemplateField>
                                </Columns>
                            </asp:GridView>

                        </div>
                    </div>
                    <div class="row">
                        <div class="col p-3">

                            <!-- This GridView uses the datatables -->

                            <asp:GridView ID="GridView2"
                                runat="server"
                                CssClass="table table-striped w-50 datatable"
                                BackColor="White"
                                AutoGenerateColumns="true"
                                ItemType="GridViewEditDemo.Classes.GridViewDemo.BookCategory"
                                OnRowDataBound="GridView_RowDataBound"
                                EnableViewState="false">
                            </asp:GridView>

                        </div>
                    </div>

                </div>

            </ContentTemplate>
        </asp:UpdatePanel>

    </form>

    <!-- script libraries -->

    <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.bundle.min.js"></script>
    <script src="https://cdn.datatables.net/1.10.9/js/jquery.dataTables.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.7.0-RC3/js/bootstrap-datepicker.min.js"></script>

    <script type="text/javascript">
        var $datatable;
        var prm = Sys.WebForms.PageRequestManager.getInstance();

        //this code is executed after the UpdatePanel is finished loading. You will need to trigger all scripts again that have bindings to DOM elements within the UpdatePanel.
        prm.add_endRequest(function () {
            InitDataTable();
            InitDatePicker();
        });

        $(document).ready(function () {
            InitDataTable();
        });

        //create the datatables
        //datatables is not used for the editable gridview because when you go in edit mode the sortorder changes,
        //and the row the user clicked to edit may change position or dissapear entirely when using paging.
        function InitDataTable() {

            //if the datatable exists, destroy it first
            if ($datatable != null) {
                $datatable.destroy();
            }

            $datatable = $('.datatable').DataTable({
                'stateSave': true, //to make sure the current sorting survives across postbacks
                'searching': false,
                'paging': false,
                'info': false
            });
        }

        //create the datepickers
        function InitDatePicker() {
            $('.bootstrap-datepicker').datepicker({
                dateFormat: 'dd/mm/yyyy',
                autoclose: true,
                todayHighlight: true
            });
        }

    </script>
</body>
</html>

The code behind.

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

namespace GridViewEditDemo
{
    public partial class Default : System.Web.UI.Page
    {
        public List MyBookList;
        public SortDirection SortDirection;
        public string SortColumn;

        protected void Page_Load(object sender, EventArgs e)
        {
            //get the gridview sort direction and column from the session
            if (Session["dir_" + GridView1.ID] != null)
            {
                SortDirection = (SortDirection)Session["dir_" + GridView1.ID];
            }
            if (Session["col_" + GridView1.ID] != null)
            {
                SortColumn = (string)Session["col_" + GridView1.ID];
            }

            //check if there is a list of books for this user
            if (Session["booklist"] != null)
            {
                MyBookList = (List)Session["booklist"];
            }

            //if there is no list or it is empty create one
            if (MyBookList == null || !MyBookList.Any())
            {
                MyBookList = GridViewDemo.GetBooks();
            }

            //sort the list of books
            MyBookList = GridViewDemo.SortBooks(MyBookList, SortColumn, SortDirection);

            //put the list in a session (normally MyBookList would just be a database table, not a list in a session)
            Session["booklist"] = MyBookList;

            //create the editable gridview with the postback check
            if (!IsPostBack)
            {
                BuildGridView(GridView1);
            }

            //create the gridview with the categories every time the page is loaded
            //this is needed to ensure the RowDataBound event is triggered so  is added to the gridview
            //without it datatables.net will not work
            //because it is loaded every time you can set the EnableViewState to False
            BuildGridView(GridView2);
        }


        private void BuildGridView(GridView gridview)
        {
            //the books gridview
            if (gridview.ID == "GridView1")
            {
                GridView1.DataSource = MyBookList;
                GridView1.DataBind();
            }

            //the categories gridview
            if (gridview.ID == "GridView2")
            {
                GridView2.DataSource = GridViewDemo.GetBookCategories();
                GridView2.DataBind();
            }
        }


        protected void GridView_RowDataBound(object sender, GridViewRowEventArgs e)
        {
            //check if the row is the header row (needed for datatables.net)
            if (e.Row.RowType == DataControlRowType.Header)
            {
                //add the thead and tbody section programatically
                e.Row.TableSection = TableRowSection.TableHeader;
            }
            else if (e.Row.RowType == DataControlRowType.DataRow && (e.Row.RowState & DataControlRowState.Edit) > 0)
            {
                //find the controls in the edit template
                var tb1 = (TextBox)e.Row.FindControl("TextBox1");
                var tb2 = (TextBox)e.Row.FindControl("TextBox2");
                var dl1 = (DropDownList)e.Row.FindControl("DropDownList1");

                //bind the categories to the dropdowlist
                dl1.DataSource = GridViewDemo.GetBookCategories();
                dl1.DataTextField = "Name";
                dl1.DataValueField = "ID";
                dl1.DataBind();

                //cast the row's dataitem back to the class Book
                var book = (GridViewDemo.Book)e.Row.DataItem;

                //set the values of the controle
                tb1.Text = book.Title;
                tb2.Text = book.Date.ToShortDateString();
                dl1.SelectedValue = book.Category.ID.ToString();
            }
        }


        protected void GridView_RowEditing(object sender, GridViewEditEventArgs e)
        {
            var gridview = (GridView)sender;

            gridview.EditIndex = e.NewEditIndex;
            BuildGridView(gridview);
        }


        protected void GridView_RowCancelingEdit(object sender, GridViewCancelEditEventArgs e)
        {
            var gridview = (GridView)sender;

            gridview.EditIndex = -1;
            BuildGridView(gridview);
        }


        protected void GridView_RowUpdating(object sender, GridViewUpdateEventArgs e)
        {
            var gridview = (GridView)sender;

            //get the id of the book from the row with DataKeyNames
            int id = Convert.ToInt32(gridview.DataKeys[e.RowIndex].Values[0]);

            //find the controls in the edit template
            var tb1 = (TextBox)gridview.Rows[e.RowIndex].FindControl("TextBox1");
            var tb2 = (TextBox)gridview.Rows[e.RowIndex].FindControl("TextBox2");
            var dl1 = (DropDownList)gridview.Rows[e.RowIndex].FindControl("DropDownList1");

            //get the book from the list
            var book = MyBookList.Where(x => x.ID == id).FirstOrDefault();

            //try to parse the date field
            DateTime date = DateTime.TryParse(tb2.Text.Trim(), out date) ? date : DateTime.Now;

            //set the values from the controls to the book
            book.Title = tb1.Text.Trim();
            book.Category = GridViewDemo.GetBookCategories().Where(x => x.ID == Convert.ToInt32(dl1.SelectedValue)).FirstOrDefault();
            book.Date = date;

            gridview.EditIndex = -1;
            BuildGridView(gridview);
        }


        protected void GridView_RowDeleting(object sender, GridViewDeleteEventArgs e)
        {
            var gridview = (GridView)sender;

            //get the id of the book from the row with DataKeyNames
            int id = Convert.ToInt32(gridview.DataKeys[e.RowIndex].Values[0]);

            //remove the book from the list and save
            MyBookList.RemoveAll(x => x.ID == id);

            gridview.EditIndex = -1;
            BuildGridView(gridview);
        }


        protected void GridView_Sorting(object sender, GridViewSortEventArgs e)
        {
            var gridview = (GridView)sender;

            //reverse the sort direction
            SortDirection = SortDirection == SortDirection.Ascending ? SortDirection.Descending : SortDirection.Ascending;

            //if the column is not the one sorted previously then always start ascending
            if (Session["col_" + GridView1.ID] != null && Session["col_" + GridView1.ID].ToString() != e.SortExpression)
            {
                SortDirection = SortDirection.Ascending;
            }

            //put the solumn and direction in the session
            Session["dir_" + GridView1.ID] = SortDirection;
            Session["col_" + GridView1.ID] = e.SortExpression;

            //re-sort the list of books
            MyBookList = GridViewDemo.SortBooks(MyBookList, e.SortExpression, SortDirection);

            BuildGridView(gridview);

            //add sort direction arrows to the clicked header cell
            for (int i = 1; i < gridview.HeaderRow.Cells.Count - 1; i++)
            {
                var linkbutton = (LinkButton)gridview.HeaderRow.FindControl("LinkButton" + i);

                if (linkbutton.Text == e.SortExpression && SortDirection == SortDirection.Descending)
                {
                    linkbutton.Text += "  ↑";
                }
                else if (linkbutton.Text == e.SortExpression)
                {
                    linkbutton.Text += "  ↓";
                }
            }
        }
    }
}