Click here to Skip to main content
Click here to Skip to main content

Bulk Edit with GridView without xxxDataSource (SqlDataSource, ObjectDataSource, etc.)

, 22 Jun 2008
Rate this:
Please Sign up or sign in to vote.
How to implement bulk edit with GridView without xxxDataSource (SqlDataSource, ObjectDataSource, etc.)

Screenshot2.jpg

Introduction

The ASP.NET GridView is a very amazing control, and it makes it very easy to implement editing on multiple rows data. Some days ago, I needed to implement bulk editing on GridView. I searched on the Internet and I found a very good article from Matt Dotson at MSDN.

Unfortunately, he uses SQLDataSource and I prefer to bind grids using the property DataSource and the method Databind. GridView is not really well designed to be used without a xxxDataSource (SQLDataSource, ObjectDataSource, etc.), especially for paging, sorting, and editing.

This tutorial is intended to explain how to implement an extended version of GridView that allows to edit/update multiple rows without any xxxDataSource.

Background

The main idea of bulk edit is quite similar to the control implemented by Matt Dotson, but I had to change most of the code because the GridView works differently using manual binding. While overriding some methods in the GridView, I found problems using manual binding because some objects are not properly initialized. I used Reflector and I discovered that most of the code within GridView strictly relies on the DataSourceID.

The control implementation

The class BulkEditGridView inherits from GridView, and it add/overrides the followings properties/methods:

  • BulkEdit: Boolean property used to maintain the state about bulk edit. This property stores the value into the ViewState.
  • CreateRow (override): used in conjunction with BulkEdit to make all rows editable.
  • BulkUpdate: used to call the GridView's RowUpdating event for all rows.
  • GetOldValue and GetNewValue: used to get the old and new values.

Overriding the method CreateRow and using the BulkEdit property, I can make all rows editable:

protected override GridViewRow CreateRow(int rowIndex, int dataSourceIndex, 
          DataControlRowType rowType, DataControlRowState rowState)
{
    GridViewRow row;

    if (this.BulkEdit)
        row = base.CreateRow(rowIndex, dataSourceIndex, 
              rowType, rowState | DataControlRowState.Edit);
    else
        row = base.CreateRow(rowIndex, dataSourceIndex, rowType, rowState);

    return row;
}

The method BulkUpdate calls the update on all rows using the GridView method, UpdateRow:

public void BulkUpdate()
{
    foreach (GridViewRow row in this.Rows)
    {
        this.UpdateRow(row.RowIndex, false);
    }
}

The methods GetOldValue and GetNewValue (use Generics) have two overloads, one each for the Template columns and DataBound columns. The old values come from the values available in the control from the cell. The new values comes from the Request collection, and are converted using the private method ConvertValue:

private T ConvertValue<T>(string strValue)
{
    object value = default(T);

    if (strValue != null)
    {
        if (typeof(T) == typeof(string))
        {
            value = strValue;
        }
        else if (typeof(T) == typeof(int))
        {
            value = Convert.ToInt32(strValue);
        }
        else if (typeof(T) == typeof(double))
        {
            value = Convert.ToDouble(strValue);
        }
        else if (typeof(T) == typeof(bool))
        {
            if (strValue.ToLower() == "on" || strValue.ToLower() == 
                        "true" || strValue.ToLower() == "1")
                value = true;
            else
                value = false;
        }
        else if (typeof(T) == typeof(DateTime))
        {
            value = Convert.ToDateTime(strValue);
        }
    }

    return (T)value;
}

public T GetOldValue<T>(int rowIndex, int cellIndex)
{
    BoundField bf = this.Columns[cellIndex] as BoundField;
    
    T retVal = default(T);

    if (bf != null)
    {
        if (bf.ReadOnly)
        {
            DataControlFieldCell cell = 
              this.Rows[rowIndex].Cells[cellIndex] as DataControlFieldCell;
            retVal = ConvertValue<T>(cell.Text);
        }
        else
        {
            Control ctrl = this.Rows[rowIndex].Cells[cellIndex].Controls[0];

            if (ctrl.GetType() == typeof(TextBox))
            {
                retVal = this.ConvertValue<T>(((TextBox)ctrl).Text);
            }
            else if (ctrl.GetType() == typeof(CheckBox))
            {
                retVal = this.ConvertValue<T>(((CheckBox)ctrl).Checked.ToString());
            }
            else if (ctrl.GetType() == typeof(DropDownList))
            {
                retVal = this.ConvertValue<T>(((DropDownList)ctrl).SelectedValue);
            }
        }
    }
    else
    {
        throw new ArgumentException("The cell selected is not a DataBoundControl!");
    }

    return retVal;
}

public T GetOldValue<T>(int rowIndex, string controlName)
{
    Control ctrl = this.Rows[rowIndex].FindControl(controlName);
    
    T retVal = default(T);

    if (ctrl != null)
    {
        if (ctrl.GetType() == typeof(TextBox))
        {
            retVal = this.ConvertValue<T>(((TextBox)ctrl).Text);
        }
        else if (ctrl.GetType() == typeof(CheckBox))
        {
            retVal = this.ConvertValue<T>(((CheckBox)ctrl).Checked.ToString());
        }
        else if (ctrl.GetType() == typeof(DropDownList))
        {
            retVal = this.ConvertValue<T>(((DropDownList)ctrl).SelectedValue);
        }
    }
    else
    {
        throw new ArgumentException("The controlName not found!");
    }

    return retVal;
}

private T GetNewValue<T>(string uniqueID)
{
    string strValue = this.Page.Request[uniqueID];
    return ConvertValue<T>(strValue);
}

public T GetNewValue<T>(int rowIndex, int cellIndex)
{
    BoundField bf = this.Columns[cellIndex] as BoundField;

    if (bf != null)
    {
        if (bf.ReadOnly)
        {
            DataControlFieldCell cell = 
              this.Rows[rowIndex].Cells[cellIndex] as DataControlFieldCell;
            return ConvertValue<T>(cell.Text);
        }
        else
        {
            string uniqueID = this.Rows[rowIndex].Cells[cellIndex].Controls[0].UniqueID;
            return this.GetNewValue<T>(uniqueID);
        }
    }
    else
    {
        throw new ArgumentException("The cell selected is not a DataBoundControl!");
    }
}

public T GetNewValue<T>(int rowIndex, string controlName)
{
    string uniqueID = this.Rows[rowIndex].FindControl(controlName).UniqueID;
    return this.GetNewValue<T>(uniqueID);
}

Using the control

In the sample web project, I used a single table (tblProducts) in a SQLite database. I used SQLite because it is very powerful, small, fast, and because my free hosting doesn't have support for database. SQLite is a file based database with most of the features available in modern RDBMSs. It supports a large subset of SQL, Views, Triggers, etc. For additional details, refer to this link.

SQLite is an open-source project created in C language, but there exists a specific ADO.NET provider that makes it very easy to use it with .NET. You can download it from here.

The web project is composed of the following files:

  1. Default.aspx: The page containing the example to use BulkEditGridView.
  2. Progress.gif: Used within the UpdateProgress control (ASP.NET AJAX Framework).
  3. ProductEntity.cs: A simple class with properties containing the data for a single product (used to bind the BulkEditGridView manually).
  4. DataAccessLayer.cs: A Singleton class used to access the database. It retrieves the list of ProductEntity instances and saves the new data.

The project uses the ASP.NET AJAX framework (UpdatePanel) to make it all more nice and fast, but it's not required for the control. The Default.aspx page contains one instance of BulkEditGridView and three buttons (Edit, Update, and Cancel) used to change the state of the grid and to update the data. The grid implements paging using the built-in paging mechanism (I know it's not very smart, but it's very easy to implement! Smile | :) ). The most important part of the code is the following:

private void RefreshButtons(bool editMode)
{
    if (editMode)
    {
        this.btnEdit.Visible = false;
        this.btnUpdate.Visible = true;
        this.btnCancel.Visible = true;
    }
    else
    {
        this.btnEdit.Visible = true;
        this.btnUpdate.Visible = false;
        this.btnCancel.Visible = false;
    }
}

protected void btnEdit_Click(object sender, EventArgs e)
{
    this.grdProducts.BulkEdit = true;
    RefreshGrid();
    RefreshButtons(true);
}

protected void btnUpdate_Click(object sender, EventArgs e)
{
    this.grdProducts.BulkEdit = true;
    RefreshGrid();

    this.grdProducts.BulkUpdate();

    this.grdProducts.BulkEdit = false;
    RefreshGrid();
    RefreshButtons(false);
}
    
protected void btnCancel_Click(object sender, EventArgs e)
{
    this.grdProducts.BulkEdit = false;
    RefreshGrid();
    RefreshButtons(false);
}

protected void grdProducts_RowUpdating(object sender, GridViewUpdateEventArgs e)
{
    int productID = this.grdProducts.GetNewValue<int>(e.RowIndex, 0);
    string productName = this.grdProducts.GetNewValue<string>(e.RowIndex, 
                                                       "txtProductName");
    double unitPrice = this.grdProducts.GetNewValue<double>(e.RowIndex, 2);
    bool discontinued = this.grdProducts.GetNewValue<bool>(e.RowIndex, 3);

    string oldProductName = this.grdProducts.GetOldValue<string>(e.RowIndex, 
                                                          "txtProductName");
    double oldUnitPrice = this.grdProducts.GetOldValue<double>(e.RowIndex, 2);
    bool oldDiscontinued = this.grdProducts.GetOldValue<bool>(e.RowIndex, 3);

    if (productName != oldProductName || unitPrice != 
             oldUnitPrice || discontinued != oldDiscontinued)
    {
        List<SQLiteParameter> parameters = new List<SQLiteParameter>();
        parameters.Add(new SQLiteParameter("@ProductID", productID));
        parameters.Add(new SQLiteParameter("@ProductName", productName));
        parameters.Add(new SQLiteParameter("@UnitPrice", unitPrice));
        parameters.Add(new SQLiteParameter("@Discontinued", discontinued));
        parameters.Add(new SQLiteParameter("@LastChange", DateTime.Now));

        DataAccessLayer.Instance.ExecuteQuery(
                        @"Update tblProducts set ProductName=@ProductName," + 
                        @" UnitPrice=@UnitPrice, Discontinued=@Discontinued, " + 
                        @"LastChange=@LastChange where ProductID=@ProductID", 
                        parameters.ToArray());
    }

In the Click event of the Edit button, I set the BulkEdit property of the grid to true, I bind the data, and I make the Edit button hidden and the Update and Cancel buttons visible. This action make all rows of grid in edit mode. The Click event on Update restores the edit mode for the grid (this is necessary to restore the control state), and the I call the BulkUpdate method of the grid. This method calls the RowUpdating event for each row in the grid. In RowUpdating, I use the methods GetOldValue and GetNewValue to compare the differences and save only the changed row. Both methods have two overloads to get the data from a simple DataBound column or from a Template column.

Points of Interest

Probably, this solution is not the best one because I have to manually compare the changes in the rows, but I haven't found a generic way to solve this problem. Any suggestions are welcome! Smile | :)

History

  • 15 June 2008 - First release.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Ferreri Gabriele (Megasoft78)
Software Developer (Senior) sparesFinder
Italy Italy
I'm an Italian Software Developer from about 15 years.
I worked a long time in south Italy (where I was born) and after 2 years in Milan and an year in UK, I'm working remotely from Italy as Senior ASP.NET C# Developer using ASP.NET Ajax technology for a UK company.
 
Check out my personal blog:
http://techcookies.net/
 
and my first Android game (Fifteen Puzzle X):
https://play.google.com/store/apps/details?id=it.megasoft78.fifteenpuzzlex

Comments and Discussions

 
QuestionError while binding DropDownList.SelectedValue in BulkEditGridView. PinmemberDinesh Prajapati1-Feb-14 8:51 
AnswerRe: Error while binding DropDownList.SelectedValue in BulkEditGridView. PinmemberFerreri Gabriele (Megasoft78)2-Feb-14 22:30 
GeneralRe: Error while binding DropDownList.SelectedValue in BulkEditGridView. PinmemberDinesh Prajapati3-Feb-14 16:48 
GeneralNewValue OldValue Pinmemberdwilsonhcf26-May-10 6:56 
GeneralRe: NewValue OldValue PinmemberFerreri Gabriele (Megasoft78)26-May-10 7:42 
QuestionHow do I get the oldValue of a hidden keysys column ? Pinmemberdwilsonhcf25-May-10 9:30 
AnswerRe: How do I get the oldValue of a hidden keysys column ? PinmemberFerreri Gabriele (Megasoft78)25-May-10 10:12 
GeneralGridView ExtractRowValues() method [modified] Pinmemberjimbo31221-May-10 4:37 
GeneralFirefox issue Pinmemberdeejayns10-Feb-10 11:53 
GeneralRe: Firefox issue PinmemberFerreri Gabriele (Megasoft78)10-Feb-10 20:50 
GeneralRe: Firefox issue Pinmemberdeejayns10-Feb-10 23:17 
GeneralRe: Firefox issue Pinmemberdeejayns10-Feb-10 23:39 
GeneralRe: Firefox issue Pinmemberdeejayns11-Feb-10 2:22 
Ok I refactored my markup and the things started to work correctly.
 
Thanks for the heads up.
 

Let me just go back to double l issue Smile | :) .
 

Gabriele is a male name, but Gabrielle is a female or I'm wrong!?
 

Just curios...
 

 
Cheers,
 

//D.
GeneralRe: Firefox issue PinmemberFerreri Gabriele (Megasoft78)11-Feb-10 5:47 
QuestionOldValue and NewValue have the same new value!? Pinmemberdeejayns26-Jan-10 2:06 
AnswerRe: OldValue and NewValue have the same new value!? Pinmemberdeejayns26-Jan-10 3:23 
GeneralRe: OldValue and NewValue have the same new value!? PinmemberFerreri Gabriele (Megasoft78)26-Jan-10 8:26 
Generalgreat job Pinmemberdeejayns16-Jan-10 13:42 
GeneralRe: great job PinmemberFerreri Gabriele (Megasoft78)16-Jan-10 22:30 
QuestionDateTime formatting issue in sample PinmemberAmyMarisa6-Jan-10 23:30 
AnswerRe: DateTime formatting issue in sample PinmemberFerreri Gabriele (Megasoft78)6-Jan-10 23:49 
GeneralRe: DateTime formatting issue in sample PinmemberAmyMarisa7-Jan-10 23:15 
QuestionWhy not use Matt Dotson's DirtyRow method? Pinmemberrodneyjoyce6-Jun-09 2:21 
AnswerRe: Why not use Matt Dotson's DirtyRow method? PinmemberFerreri Gabriele (Megasoft78)7-Jun-09 0:18 
GeneralRe: Why not use Matt Dotson's DirtyRow method? Pinmemberrodneyjoyce7-Jun-09 20:48 
GeneralGet Value Using Dropdownlist Pinmemberpanux_s29-Mar-09 14:32 
GeneralRe: Get Value Using Dropdownlist Pinmemberpanux_s29-Mar-09 15:49 
Generaldidn't see pagination answered here Pinmemberatarime3-Mar-09 9:59 
GeneralRe: didn't see pagination answered here PinmemberFerreri Gabriele (Megasoft78)4-Mar-09 20:23 
GeneralNew line in empty datasource Pinmembereduseixas6-Jan-09 1:06 
AnswerRe: New line in empty datasource PinmemberFerreri Gabriele (Megasoft78)6-Jan-09 1:30 
GeneralRe: New line in empty datasource Pinmembereduseixas6-Jan-09 1:43 
GeneralRe: New line in empty datasource Pinmembereduseixas6-Jan-09 3:10 
GeneralRe: New line in empty datasource PinmemberTomIn0182125-Jan-09 5:28 
QuestionUse dropdown list in a column Pinmembermariagp2-Dec-08 6:54 
AnswerRe: Use dropdown list in a column PinmemberFerreri Gabriele (Megasoft78)2-Dec-08 8:17 
QuestionRe: Use dropdown list in a column Pinmembermariagp2-Dec-08 9:45 
AnswerRe: Use dropdown list in a column PinmemberFerreri Gabriele (Megasoft78)2-Dec-08 10:24 
QuestionUsing Bulk Edit Gridview from VB Pinmemberxavier33329-Aug-08 8:33 
AnswerRe: Using Bulk Edit Gridview from VB PinmemberFerreri Gabriele (Megasoft78)30-Aug-08 3:22 
GeneralRe: Using Bulk Edit Gridview from VB Pinmemberxavier33331-Aug-08 2:27 
QuestionSlow in edit mode with large amount of Data &gt; 100 rows Pinmember222556628-Aug-08 7:39 
AnswerRe: Slow in edit mode with large amount of Data &gt; 100 rows PinmemberFerreri Gabriele (Megasoft78)28-Aug-08 8:11 
Questionpartial bulk edit? Pinmemberstockid27-Aug-08 14:14 
AnswerRe: partial bulk edit? PinmemberFerreri Gabriele (Megasoft78)27-Aug-08 20:15 
GeneralRe: partial bulk edit? Pinmemberstockid28-Aug-08 8:31 
GeneralRe: partial bulk edit? PinmemberFerreri Gabriele (Megasoft78)28-Aug-08 11:34 
GeneralRe: partial bulk edit? Pinmemberstockid28-Aug-08 12:27 
GeneralExcellent! Pinmemberbstinson27-Aug-08 7:35 
GeneralRe: Excellent! PinmemberTomIn0182124-Jan-09 12:01 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140827.1 | Last Updated 22 Jun 2008
Article Copyright 2008 by Ferreri Gabriele (Megasoft78)
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid