ASP.NET Today: GridView Gripes

Published 30 June 05 03:15 PM | despos

The following was a rather common task to accomplish with DataGrids. Users click a button in a button column (say, Add to Cart), a server-side event fire (ItemCommand), you handle it and do what's needed. This basic pattern doesn't change with GridViews, but some details are different and require different approaches.

First and foremost, the index of the clicked row is not carried by the event data structure (as with DataGrids), but can be found through the CommandArgument property. Here's how:

protected void GridView1_RowCommand(object sender, GridViewCommandEventArgs e)
{
    if (e.CommandName.Equals("Add"))
    {
      int index = Convert.ToInt32(e.CommandArgument);
      :
    }
}

The Beta 2 documentation mentions you should put the index in the CommandArgument manually by writing a RowCreated event handler. Some sample code in the same Beta 2 documentation uses the code above and it works, at least for GridViews.

At this point, you need to retrieve in the postback some values to identify the corresponding data row--the one whose data you need to process.

  • Option #1: You set one or more key fields to be carried on through the DataKeys collection. Since DataKeys supports multiple fields (it is only one with DataGrids), you can see it as a kind of personal cargo collection for you to retrieve quick-to-use data. You simply use the key field(s) if you need to go down to the database to obtain fresh data.
  • Option #2: With DataGrids you can try a better approach: get the data item index (not the index of the row in the grid, but the index of the corresponding data item in the data source), get the bound enumerable set of rows, and retrieve the data. This works with GridView if you bind through DataSource, because DataSource can return a non-empty collection of data--typically, a DataView object. If you bind the GridView to a data source control through DataSourceID, it doesn't work because the real set of bound rows are not accessible programmatically. 

The code below shows how to retrieve (without running a new query) information about the product in the row where you clicked a Add command button.

     protected void GridView1_RowCommand(object sender, GridViewCommandEventArgs e)
    {
        if (e.CommandName.Equals("Add"))
        {
            // Get the index of the clicked row
            int index = Convert.ToInt32(e.CommandArgument);

            // Add the item to the shopping cart
            AddToShoppingCart(index);
        }
    }

    private void AddToShoppingCart(int rowIndex)
    {
        DataKey data = GridView1.DataKeys[rowIndex];

        ShoppingItem item = new ShoppingItem();
        item.NumberOfItems = 1;
        item.ProductID = (int) data.Values["productid"];
        item.ProductName = data.Values["productname"].ToString();
        item.UnitPrice = (decimal) data.Values["unitprice"];
        MyShoppingCart.Add(item);

        ShoppingCartGrid.DataSource = MyShoppingCart;
        ShoppingCartGrid.DataBind();
    }

In my sample page, ShoppingCartGrid is a grid I use to show the contents of the shopping cart. It is bound to a custom collection object of type ShoppingCart stored in the Session and retrieved through the public MyShoppingCart. Finally, DataKeys. By design, it returns an array of DataKey objects with as many fields as there are elements in the DataKeyNames property of the GridView. For the code above to work, you need the following GridView declaration:

<asp:GridView ID="GridView1" runat="server" DataSourceID="SqlDataSource1" BackColor="white"  
    DataKeyNames
="productid,productname,unitprice"
    AutoGenerateColumns="false" AllowPaging="true"
    OnRowCommand
="GridView1_RowCommand">
    <Columns>
       
<asp:boundfield datafield="productname" headertext="Product" />
        <asp:boundfield datafield="quantityperunit" headertext="Packaging" />
        <asp:boundfield datafield="unitprice" headertext="Price" DataFormatString="{0:c}" />
        <asp:buttonfield buttontype="Button" text="Add" CommandName="Add" />
    </Columns>
</asp:GridView>

 

 

 

 

Comments

# Kevin Daly said on June 30, 2005 11:34 AM:

Oh no, major disillusionment setting in....
Admittedly it's 3:30 am and I'm sleep-deprived and suffering from ASP.NET-booking-site-about-to-go-live jitters, but what in God's name inspired them to leave out the index? Isn't that pretty fundamental for a tabular control?
So much for 70% less code (or whatever it was supposed to be). Sigh.

I wonder if that's a case of the dreaded Best Practices at work...

I'm definitely in a grizzly mood right now.

# DinoE said on June 30, 2005 11:51 AM:

Hi Kevin,
I was feeling more or less like you and said "Dino, you should be missing something important here...". And it was 11:30 AM, so no excuses :-)

Next, I figured out that at least I was not the only one raising these issues. Another guy who's not known to be lame in ASP.NET development ran into something similar, as you can read here:

http://pluralsight.com/blogs/fritz/archive/2005/06/24/11975.aspx

# Andrew Robinson said on June 30, 2005 02:17 PM:

Dino, Another grip and I think quite legitimate:

If your gridview is bound to an empty dataset / datatable, it hides the footer. I usually put my insert stuff into the footer. If a user deletes all of the records in a table, they can't insert any new ones. There is a empty data template, but this really doesn't work well and would require additional logic to handle the empty condition.

My workaround:

private bool LoadDataEmpty = false;
private void LoadData()
{
PositionData positionData = new PositionData();

DataTable dataTable = positionData.Read(DepartmentID);

if (dataTable.Rows.Count == 0) {
dataTable.Rows.Add(dataTable.NewRow());
LoadDataEmpty = true;
}

GridViewMain.DataSource = dataTable;
GridViewMain.DataBind();
}

protected void GridViewMain_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow && LoadDataEmpty) {
e.Row.Visible = false;
}
}

Kind of tricks the gridview and it works, but we shouldn't have to go to such lengths!

-Andrew

# SinkableHail said on July 5, 2005 01:09 PM:

Just recently upgraded to beta 2. Found that if you hide a column in the GridView there is no way to access the information from that column. If the column is not hidden there is not problem.

Wondering what a possible solution to something like this is.

# sgfh said on June 7, 2007 12:29 AM:

fghm

# wledt said on June 17, 2007 12:18 AM:

<a href= volny.cz/.../anne-hathaway-nude.html >anne hathaway nude</a> <a href= volny.cz/.../creampie-pussys.html >creampie pussys</a> <a href= volny.cz/.../jessica-rabbit-porn.html >jessica rabbit porn</a> <a href= volny.cz/.../kim-possible-porn.html >kim possible porn</a> <a href= volny.cz/.../natalie-portman-nude.html >natalie portman nude</a>

# aatnl said on June 17, 2007 01:24 AM:

<a href= volny.cz/.../pornstar-gals.html >pornstar gals</a> <a href= volny.cz/.../free-milf-porn.html >free milf porn</a> <a href= volny.cz/.../european-porn.html >european porn</a> <a href= volny.cz/.../nasty-porn.html >nasty porn</a> <a href= volny.cz/.../soft-core-porn.html >soft core porn</a>

# Walker said on August 5, 2007 01:25 AM:

buy-tramadol--online.info/.../online-tramadol-with-free-shipping.php

# fabiola-wr said on January 26, 2008 09:48 AM:

<a href= http://index1.scukam.com >exploitation films</a>

Leave a Comment

(required) 
(required) 
(optional)
(required)