I'm having some problems with one of our webshops. A Telerik radgrid works as the cart and lists all products currently in the cart. The paging on the Radgrid correctly splits the cart view into different pages. The problem occurs when one manually tries to change the amount of a product, but only if the product is on the LAST page of the radgrid and only if the number of products are less than the page size limit.
I have figured it out as much as the radgrid believes there are always an even amount of products, based on the radgrid's page size.
The crash occurs in the tbQuantity_TextChanged event handler, more specific when var shopItemID = Convert.ToInt32(item.GetDataKeyValue("ID"));
is called for an item that doesn't exist on this page, but on another page.
The exception is
System.ArgumentOutOfRangeException was unhandled by user code
Message=Index was
out
of range. Must be non-negative and less than the size of the collection.
Parameter name: index
<%@ Control Language=
"C#"
AutoEventWireup=
"true"
CodeBehind=
"ShopItems.ascx.cs"
Inherits=
"Litho.Framework.Web.Modules.Shop.ShopItems"
%>
<%@ Register Assembly=
"Telerik.Web.UI"
Namespace=
"Telerik.Web.UI"
TagPrefix=
"telerik"
%>
<telerik:RadGrid
ID=
"gvCartItems"
runat=
"server"
AutoGenerateColumns=
"False"
AllowPaging=
"True"
PageSize=
"5"
Skin=
"Default"
GridLines=
"None"
AllowFilteringByColumn=
"False"
AllowSorting=
"True"
ShowFooter=
"True"
OnNeedDataSource=
"gvCartItems_NeedDataSource"
OnItemCreated=
"gvCartItems_ItemCreated"
OnItemDataBound=
"gvCartItems_ItemDataBound"
OnDeleteCommand=
"gvCartItems_DeleteCommand"
>
<MasterTableView Width=
"100%"
NoMasterRecordsText=
"Inga artiklar"
ShowHeadersWhenNoRecords=
"false"
DataKeyNames=
"ID"
>
<Columns>
<telerik:GridButtonColumn ButtonType=
"ImageButton"
CommandName=
"Delete"
Text=
"Radera"
UniqueName=
"DeleteColumn"
HeaderStyle-Width=
"20"
/>
<telerik:GridTemplateColumn HeaderText=
"Antal"
HeaderStyle-Width=
"110px"
DataField=
"Quantity"
UniqueName=
"Quantity"
Aggregate=
"Sum"
FooterText=
"Totalt antal: "
>
<ItemTemplate>
<telerik:RadNumericTextBox
ID=
"tbQuantity"
runat=
"server"
AutoPostBack=
"true"
Width=
"70px"
MinValue=
"1"
Visible=
"false"
ShowSpinButtons=
"true"
IncrementSettings-InterceptArrowKeys=
"false"
NumberFormat-GroupSizes=
"9"
NumberFormat-DecimalDigits=
"0"
IncrementSettings-InterceptMouseWheel=
"true"
NumberFormat-AllowRounding=
"False"
OnTextChanged=
"tbQuantity_TextChanged"
>
<EnabledStyle HorizontalAlign=
"Right"
/>
</telerik:RadNumericTextBox>
<telerik:RadComboBox ID=
"ddlQuantity"
DataTextField=
"Quantity"
DataValueField=
"Quantity"
Visible=
"false"
runat=
"server"
Width=
"50px"
AutoPostBack=
"true"
OnSelectedIndexChanged=
"ddlQuantity_SelectedIndexChanged"
/>
</ItemTemplate>
</telerik:GridTemplateColumn>
<telerik:GridBoundColumn HeaderText=
"Artnr"
ReadOnly=
"True"
DataField=
"ArticleNumber"
UniqueName=
"ArticleNumber"
HeaderStyle-Width=
"80"
/>
<telerik:GridTemplateColumn HeaderText=
"Artikel"
UniqueName=
"Title"
ShowFilterIcon=
"false"
>
<ItemTemplate>
<asp:HyperLink ID=
"hlTitle"
runat=
"server"
/>
</ItemTemplate>
</telerik:GridTemplateColumn>
<telerik:GridTemplateColumn HeaderText=
"a`pris"
UniqueName=
"Price"
ShowFilterIcon=
"false"
>
<ItemTemplate>
<asp:Label ID=
"lblUnitprice"
runat=
"server"
/>
</ItemTemplate>
</telerik:GridTemplateColumn>
<telerik:GridTemplateColumn HeaderText=
"Totalpris"
UniqueName=
"Totalprice"
ShowFilterIcon=
"false"
>
<ItemTemplate>
<asp:Label ID=
"lblTotalPrice"
runat=
"server"
/>
</ItemTemplate>
</telerik:GridTemplateColumn>
</Columns>
</MasterTableView>
<FooterStyle Font-Bold=
"true"
BackColor=
"#e6e6e6"
/>
<PagerStyle Mode=
"NextPrevAndNumeric"
/>
<FilterMenu EnableTheming=
"True"
>
<CollapseAnimation Duration=
"200"
Type=
"OutQuint"
/>
</FilterMenu>
</telerik:RadGrid>
<asp:PlaceHolder ID=
"phItemSummary"
runat=
"server"
/>
Code behind
using
System;
using
System.Collections.Generic;
using
System.Web.UI;
using
System.Web.UI.WebControls;
using
Litho.Framework.BusinessLayer;
using
Litho.Framework.BusinessLayer.Base;
using
Litho.Framework.BusinessLayer.Base.Settings;
using
Litho.Framework.BusinessLayer.Modules.Shop;
using
Litho.Framework.PresentationLayer;
using
Litho.Framework.ServiceLayer;
using
Telerik.Web.UI;
namespace
Litho.Framework.Web.Modules.Shop
{
public
partial
class
ShopItems : UserControl
{
IShopItemHolder _shopItemHolder =
null
;
bool
_readMode =
true
;
SessionHelper _sessionHelper =
new
SessionHelper();
#region Public methods
public
void
LoadItems(IShopItemHolder shopItemHolder,
bool
readMode)
{
_shopItemHolder = shopItemHolder;
_readMode = readMode;
gvCartItems.Rebind();
loadItemsSummary();
}
private
void
loadItemsSummary()
{
phItemSummary.Controls.Clear();
var ucItemSummary = (ShopItemsSummary)Page.LoadControl(
"~/Modules/Shop/ShopItemsSummary.ascx"
);
ucItemSummary.LoadItemSummary(_shopItemHolder);
phItemSummary.Controls.Add(ucItemSummary);
}
#endregion
#region Events
protected
void
gvCartItems_NeedDataSource(
object
source, GridNeedDataSourceEventArgs e)
{
gvCartItems.DataSource = _shopItemHolder.Items;
}
protected
void
gvCartItems_ItemCreated(
object
sender, GridItemEventArgs e)
{
if
(e.Item
is
GridDataItem)
{
var item = (GridDataItem)e.Item;
var btnDelete = (ImageButton)item[
"DeleteColumn"
].Controls[0];
btnDelete.ImageUrl =
string
.Format(
"~/Base/Themes/{0}/Images/Icons16x16/iconDelete.png"
, SettingsManager.GetGlobalSettings().AdminTheme);
btnDelete.Visible = !_readMode;
}
}
protected
void
gvCartItems_ItemDataBound(
object
sender, GridItemEventArgs e)
{
if
(e.Item
is
GridDataItem)
{
var shopItem = (IShopItem)e.Item.DataItem;
var hlTitle = (HyperLink)e.Item.FindControl(
"hlTitle"
);
var lblUnitPrice = (Label)e.Item.FindControl(
"lblUnitprice"
);
var lblTotalPrice = (Label)e.Item.FindControl(
"lblTotalPrice"
);
if
(!shopItem.IsExternal)
{
var tbQuantity = (RadNumericTextBox)e.Item.FindControl(
"tbQuantity"
);
tbQuantity.ShowSpinButtons = !_readMode;
tbQuantity.Text = shopItem.Quantity.ToString();
tbQuantity.Visible =
true
;
tbQuantity.Enabled = !_readMode;
}
else
{
if
(!_readMode)
{
var ddlQuantity = (RadComboBox)e.Item.FindControl(
"ddlQuantity"
);
ddlQuantity.DataSource = shopItem.PriceCollection;
ddlQuantity.DataBind();
ddlQuantity.SelectedValue = shopItem.Quantity.ToString();
ddlQuantity.Visible =
true
;
}
}
if
(!shopItem.IsExternal)
{
var parameters =
new
Dictionary<
string
,
string
>();
parameters.Add(KeyMaster.RequestParamsNames.Modules.Shop.PRODUCT_ID, shopItem.ID.ToString());
hlTitle.NavigateUrl =
new
FWContent().GetContentUrl(ModuleIDConstant.SHOP, ContentIDConstant.Shop.PRODUCT_VIEW, parameters);
}
hlTitle.Text = shopItem.Title;
lblUnitPrice.Text = shopItem.Price.ToString(
"0.00"
) +
" SEK"
;
lblTotalPrice.Text = shopItem.GetCost(
false
).ToString(
"0.00"
) +
" SEK"
;
}
}
protected
void
tbQuantity_TextChanged(
object
sender, EventArgs e)
{
if
(!_readMode)
{
RadNumericTextBox tbQuantity;
foreach
(GridDataItem item
in
gvCartItems.Items)
{
if
(item
is
GridDataItem)
{
tbQuantity = item.FindControl(
"tbQuantity"
)
as
RadNumericTextBox;
var shopItemID = Convert.ToInt32(item.GetDataKeyValue(
"ID"
));
var shopItem = _sessionHelper.CurrentCart.CartItems.Find(x => x.ID == shopItemID);
if
(!shopItem.IsExternal)
{
_sessionHelper.CurrentCart.CartItems.Find(x => x.ID == shopItemID).Quantity = Convert.ToInt32(tbQuantity.Text);
_sessionHelper.CurrentCart.CartItems.Find(x => x.ID == shopItemID).TotalPrice = _sessionHelper.CurrentCart.GetItemCost(shopItemID,
false
);
_shopItemHolder = _sessionHelper.CurrentCart;
}
}
}
loadItemsSummary();
gvCartItems.Rebind();
}
}
protected
void
gvCartItems_DeleteCommand(
object
source, GridCommandEventArgs e)
{
if
(!_readMode)
{
var cartItemID = (
int
)e.Item.OwnerTableView.DataKeyValues[e.Item.ItemIndex][
"ID"
];
_sessionHelper.CurrentCart.DeleteItem(cartItemID);
_shopItemHolder = _sessionHelper.CurrentCart;
if
(_sessionHelper.CurrentCart.Items.Count == 0)
{
Response.Redirect(
new
FWContent().GetContentUrl(ModuleIDConstant.SHOP, ContentIDConstant.Shop.CART));
}
else
{
gvCartItems.Rebind();
loadItemsSummary();
}
}
}
protected
void
ddlQuantity_SelectedIndexChanged(
object
sender, EventArgs e)
{
RadComboBox ddlQuantity;
foreach
(GridDataItem item
in
gvCartItems.Items)
{
if
(item
is
GridDataItem)
{
ddlQuantity = item.FindControl(
"ddlQuantity"
)
as
RadComboBox;
var cartItemID = Convert.ToInt32(item.GetDataKeyValue(
"ID"
));
var cartItem = _sessionHelper.CurrentCart.CartItems.Find(x => x.ID == cartItemID);
if
(cartItem.IsExternal)
{
cartItem.Quantity = Convert.ToInt32(ddlQuantity.Text);
cartItem.TotalPrice = getProductPrice(cartItem);
}
}
}
loadItemsSummary();
gvCartItems.Rebind();
}
#endregion
private
double
getProductPrice(CartItem cartitem)
{
foreach
(var price
in
cartitem.PriceCollection)
{
if
(price.Quantity == cartitem.Quantity)
{
return
price.Price;
}
}
throw
new
Exception();
// todo lägg till customexception
}
}
}
I've been trying to work something out with custom paging, but no luck so far, so I'm still using this code, only changed PageSize to 100 so the shop won't crash that easily. ;)
Thanks in advance!