Answered by:
Gridview Row Updating Problem - Index was out of range

Question
-
User-1501204699 posted
Hello there,
I have a gridview as follows
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" AllowSorting="True"
CssClass="datatable" CellPadding="0" BorderWidth="0px" GridLines="None" OnRowDeleting="GridView1_RowDeleting"
OnRowEditing="GridView1_RowEditing" OnRowUpdating="GridView1_RowUpdating" OnRowCancelingEdit="GridView1_RowCancelingEdit">
<PagerStyle CssClass="pager-row" />
<RowStyle CssClass="row" />
<PagerSettings Mode="NumericFirstLast" PageButtonCount="7" FirstPageText="«"
LastPageText="»" />
<Columns>
<asp:BoundField DataField="ID" HeaderText="ID" ReadOnly="True">
<HeaderStyle HorizontalAlign="Left" />
</asp:BoundField>
<asp:BoundField DataField="Name" HeaderText="Name" ReadOnly="True">
<HeaderStyle HorizontalAlign="Left" />
</asp:BoundField>
<asp:BoundField DataField="Price" HeaderText="Price" ReadOnly="True">
<HeaderStyle HorizontalAlign="Left" />
</asp:BoundField>
<asp:BoundField DataField="Quantity" HeaderText="Quantity">
<HeaderStyle HorizontalAlign="Left" />
</asp:BoundField>
<asp:BoundField DataField="Total" HeaderText="Total" ReadOnly="True">
<HeaderStyle HorizontalAlign="Left" />
</asp:BoundField>
<asp:CommandField EditText="Change" ShowDeleteButton="True" ShowEditButton="True">
<ItemStyle BorderStyle="None" />
<HeaderStyle BorderStyle="None" />
</asp:CommandField>
</Columns>
<EmptyDataTemplate>
<p>
Your Shopping Cart is empty.</p>
</EmptyDataTemplate>
</asp:GridView>
and the code behind is
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
CheckTimeStamps()
If Session("cart") Is Nothing Then
Cart = New ShoppingCart()
Session("cart") = Cart
Else
Cart = Session("cart")
End If
GridView1.DataSource = Cart.GetItems()
If Not IsPostBack Then
GridView1.DataBind()
End If
btnCheckOut.Enabled = (Cart.Count > 0)
End Sub
Protected Sub GridView1_RowCancelingEdit(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewCancelEditEventArgs) Handles GridView1.RowCancelingEdit
e.Cancel = True
GridView1.EditIndex = -1
GridView1.DataBind()
End Sub
Protected Sub GridView1_RowDeleting(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewDeleteEventArgs) Handles GridView1.RowDeleting
cart.DeleteItem(e.RowIndex)
GridView1.DataBind()
End Sub
Protected Sub GridView1_RowEditing(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewEditEventArgs) Handles GridView1.RowEditing
GridView1.EditIndex = e.NewEditIndex
GridView1.DataBind()
End Sub
Protected Sub GridView1_RowUpdating(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewUpdateEventArgs) Handles GridView1.RowUpdating
Dim cell As DataControlFieldCell
cell = GridView1.Rows(e.RowIndex).Controls(3)
Dim t As TextBox = cell.Controls(0)
Try
Dim q As Integer
q = Integer.Parse(t.Text)
cart.UpdateQuantity(e.RowIndex, q)
Catch ex As FormatException
e.Cancel = True
End Try
GridView1.EditIndex = -1
GridView1.DataBind()
End Sub
But whenever i try to delete or update items i get an "Specified argument was out of range of valid values.Parameter name: index" and "Index was out of range. Must be non negative and less than the size of the collection. Parameter name: index". I dont know how to solve this.
Thanks in advance.
Saturday, July 18, 2009 6:47 PM
Answers
-
User232384818 posted
well, keep the previous changes I asked you to make. In addition, you also have erratically wired the event handlers for gridview ItemUpdating,ItemDeleting, RowCancelingEdit, RowEditing..... all these event handlers you have registered two times.
So this means, everytime you click the delete button for example, the ItemDeleting will fire 2 times. This is the main cause for your Index out of range error as the delete, update is performed 2 times for each postback.
Please note how you use the Handles clause eg :
Protected Sub GridView1_RowDeleting(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewDeleteEventArgs) Handles GridView1.RowDeleting
cart.DeleteItem(e.RowIndex)
GridView1.DataBind()
End Subnote that the handles clause registers the event handler once. But you register it a second time declaratively, look here :
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" AllowSorting="True"
CssClass="datatable" CellPadding="0" BorderWidth="0px" GridLines="None" OnRowDeleting="GridView1_RowDeleting"
OnRowEditing="GridView1_RowEditing" OnRowUpdating="GridView1_RowUpdating" OnRowCancelingEdit="GridView1_RowCancelingEdit">
you pretty much do this for all your event handlers. Also in your page_load you use the handles clause, remove that since by default, AutoEventWireup="true" for your page. This means, all you need to do is add a method Page_Load eg :
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) <strike>Handles Me.Load</strike>
End suband the framework will do the rest.
Remove the handles clause for the rest of your event handlers as you have already wired the event handlers declaratively. And your code should just work :-)
Unfortunately, c# is my main language and i was a bit slow to catch this mistake in your code.
have a good weekend.
- Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
Sunday, July 19, 2009 10:04 AM
All replies
-
User232384818 posted
hi, please note the erratic behavior of your application eg :
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
CheckTimeStamps()
If Session("cart") Is Nothing Then
Cart = New ShoppingCart()
Session("cart") = Cart
Else
Cart = Session("cart")
End If
GridView1.DataSource = Cart.GetItems()
If Not IsPostBack Then
GridView1.DataBind()
End If
btnCheckOut.Enabled = (Cart.Count > 0)
End Sub
do you see how in page_load you are setting the datasource, specifically this line :
GridView1.DataSource = Cart.GetItems()
ok, you set the datasource early in page_load, for a while this is fine.
But later on, during a delete operation for example :
Protected Sub GridView1_RowDeleting(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewDeleteEventArgs) Handles GridView1.RowDeleting
cart.DeleteItem(e.RowIndex)
GridView1.DataBind()
End Sub
note again the line :
cart.DeleteItem(e.RowIndex)
yoa are deleting an item. However in the next line when you databind, you bind stale data. Because page_load fires prior to the postback events and this is true for paging/deleting cancelling etc.
so instead you want to delete, then get fresh data and pass that to your gridviews datasource and then only bind. Or you end up with a record that was already deleted, which could result in the index was out of range behavior :-)
so try to make those changes and report back. More specifically, try to be more explicit, an extra line won't hurt your code eg :
<strike>GridView1.DataSource = Cart.GetItems()</strike>
If Not IsPostBack Then
GridView1.DataSource = Cart.GetItems()
GridView1.DataBind()
End If
eg :
Protected Sub GridView1_RowDeleting(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewDeleteEventArgs) Handles GridView1.RowDeleting
cart.DeleteItem(e.RowIndex)
GridView1.DataSource = Cart.GetItems()
GridView1.DataBind()
End Sub
and so on.Apart from this, i note nothing out of the ordinary
Saturday, July 18, 2009 7:44 PM -
User-1501204699 posted
Thanks a lot alessandro for a very nice explanation. But i am still not able to solve my problems..
I tried your suggestion in the row deleting event but when i did , it just didnt delete.
and when i do it the way i was doing it throws error at this line in my VB Class Shopping Cart. Whose code i have pasted below.
Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index
Line 31: End Sub
Line 32: Public Sub DeleteItem(ByVal index As Integer)
Line 33: _cart.RemoveAt(index)
Line 34: End Sub
Line 35: Public ReadOnly Property Count() As Integer
And i am still getting this error when i update
Specified argument was out of the range of valid values.
Parameter name: index
Line 40: Dim cell As DataControlFieldCell
Line 41: cell = GridView1.Rows(e.RowIndex).Controls(3)
Line 42: Dim t As TextBox = cell.Controls(0)
Line 43: Try
Line 44: Dim q As Integer
Here is my shopping cart class
Imports Microsoft.VisualBasic
Imports System.Collections.Generic
Public Class ShoppingCart
Private _cart As List(Of CartItem)
Public Sub New()
_cart = New List(Of CartItem)()
End Sub
Public Function GetItems() As List(Of CartItem)
Return _cart
End Function
Public Sub AddItem(ByVal id As String, _
ByVal name As String, _
ByVal price As Decimal)
Dim itemFound As Boolean = False
For Each item As CartItem In _cart
If item.ID = id Then
item.Quantity += 1
itemFound = True
End If
Next
If Not itemFound Then
Dim item As CartItem
item = New CartItem(id, name, price, 1)
_cart.Add(item)
End If
End Sub
Public Sub UpdateQuantity(ByVal index As Integer, ByVal quantity As Integer)
Dim item As CartItem
item = _cart(index)
item.Quantity = quantity
End Sub
Public Sub DeleteItem(ByVal index As Integer)
_cart.RemoveAt(index)
End Sub
Public ReadOnly Property Count() As Integer
Get
Return _cart.Count
End Get
End Property
End Class
Thanks a lot alessandro. I am really stuck at it.Sunday, July 19, 2009 6:07 AM -
User232384818 posted
well, keep the previous changes I asked you to make. In addition, you also have erratically wired the event handlers for gridview ItemUpdating,ItemDeleting, RowCancelingEdit, RowEditing..... all these event handlers you have registered two times.
So this means, everytime you click the delete button for example, the ItemDeleting will fire 2 times. This is the main cause for your Index out of range error as the delete, update is performed 2 times for each postback.
Please note how you use the Handles clause eg :
Protected Sub GridView1_RowDeleting(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewDeleteEventArgs) Handles GridView1.RowDeleting
cart.DeleteItem(e.RowIndex)
GridView1.DataBind()
End Subnote that the handles clause registers the event handler once. But you register it a second time declaratively, look here :
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" AllowSorting="True"
CssClass="datatable" CellPadding="0" BorderWidth="0px" GridLines="None" OnRowDeleting="GridView1_RowDeleting"
OnRowEditing="GridView1_RowEditing" OnRowUpdating="GridView1_RowUpdating" OnRowCancelingEdit="GridView1_RowCancelingEdit">
you pretty much do this for all your event handlers. Also in your page_load you use the handles clause, remove that since by default, AutoEventWireup="true" for your page. This means, all you need to do is add a method Page_Load eg :
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) <strike>Handles Me.Load</strike>
End suband the framework will do the rest.
Remove the handles clause for the rest of your event handlers as you have already wired the event handlers declaratively. And your code should just work :-)
Unfortunately, c# is my main language and i was a bit slow to catch this mistake in your code.
have a good weekend.
- Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
Sunday, July 19, 2009 10:04 AM -
User-1501204699 posted
Thank you so much alessandro.. Your solution worked like a charm..
And ya thank you so much for such a nice, clean and elaborate advice. I dont think i would have learnt anything like what i just did from your post from any book... It was really good..
You too have a nice weekend
Sunday, July 19, 2009 5:45 PM -
User232384818 posted
and you sir are a very kind man. Your welcome
Many thanks for reporting back :-)
Sunday, July 19, 2009 6:44 PM