ASP.NET Out of bound exception accessing a row in gridview - c#

I have this asp.net control:
<div class="field" style="text-align: center;">
<asp:GridView ID="gvTags" runat="server" AutoGenerateColumns = "false"
OnRowDataBound="OnRowDataBound" OnRowDeleting="OnRowDelete">
<Columns>
<asp:BoundField DataField="tagId" HeaderText="Id" />
<asp:BoundField DataField="name" HeaderText="<%$ Resources:Common, tlblTags %>" />
<asp:CommandField ShowDeleteButton="True" ButtonType="Button" />
</Columns>
</asp:GridView>
</div>
And I bind my datasource like this from a collection retrieved from database:
this.gvTags.DataSource = product.Tags;
this.gvTags.DataBind();
In the OnRowDataBound I create a popup to show up when the delete button is clicked, so the code is this:
protected void OnRowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
string item = e.Row.Cells[1].Text;
foreach (Button button in e.Row.Cells[2].Controls.OfType<Button>())
{
if (button.CommandName == "Delete")
{
button.Attributes["onclick"] = "if(!confirm('Do you want to delete " + item + "?')){ return false; };";
}
}
}
}
This is working fine, I can see the three rows in my browser and all works as expected (the popup is showing with the correct information etc). But the problem is when I accept the popup so the OnRowDelete event is fired.
I want to remove a Tag from the database with a specific tagId, so I need to get the position 0 at the current row, so this is my code to do that:
protected void OnRowDelete(object sender, GridViewDeleteEventArgs e)
{
int index = Convert.ToInt32(e.RowIndex);
GridViewRow row = this.gvTags.Rows[index];
long tagId = Convert.ToInt64(row.Cells[0].Text);
/* Remove the tag */
productService.RemoveTag(productId, tagId);
/* Delete the row from gridview */
this.gvTags.DeleteRow(index);
}
The error I'am getting is in this line: GridViewRow row = this.gvTags.Rows[index];. I'm getting System.ArgumentOutOfRangeException but I don't understand why. The index variable takes a valid int value like 0, 1...
Any ideas on what I'am doing wrong? Thanks.

Related

How to delete a row without using OnRowDeleted/OnRowDeleting in gridview

Code Behind
public void lbDelete_Click(object sender, EventArgs e)
{
LinkButton lb = sender as LinkButton;
GridViewRow gvrow = lb.NamingContainer as GridViewRow;
gvsize.DeleteRow(gvrow.RowIndex);
}
GridView:
<asp:GridView ID="gvsize" runat="server" ShowFooter="True" CellPadding="1" CellSpacing="2" AutoGenerateColumns="false" GridLines="Horizontal">
<asp:TemplateField HeaderText="Action">
<ItemTemplate>
<asp:LinkButton ID="lnkdelete" runat="server" ForeColor="Blue" OnClick="lbDelete_Click">Delete</asp:LinkButton>
</ItemTemplate>
</asp:TemplateField>
</asp:GridView >
There are 2 rows in my gridview which I need to delete the row using the function above.
It throws an error "gvsize" RowDeletingEvent was not handled properly.
Is that necessary to use OnRowDeleted/OnRowDeleting in gridview which I feel not necessary??
As stated in How to delete row from gridview?
You are deleting the row from the gridview but you are then going and
calling databind again which is just refreshing the gridview to the
same state that the original datasource is in.
Either remove it from the datasource and then databind, or databind
and remove it from the gridview without redatabinding.
You can use row databound event to accomplish this task.
<asp:LinkButton ID="lnkBtnDel" runat="server" CommandName="DeleteRow" OnClientClick="return confirm('Are you sure you want to Delete this Record?');"">Delete</asp:LinkButton>
and in the rowdatabound event you can have
protected void GridView1_RowCommand(object sender, GridViewCommandEventArgs e)
{
if (e.CommandName == "DeleteRow")
{
//incase you need the row index
int rowIndex = ((GridViewRow)((LinkButton)e.CommandSource).NamingContainer).RowIndex;
//followed by your code
}
}
Try this to delete row
protected void GridView1_RowDeleting(object sender, GridViewDeleteEventArgs e)
{
dt.Rows.RemoveAt(e.RowIndex);
GridView1.DataSource = dt;
GridView1.DataBind();
}
You can also delete row from another method using Template Column
ASPX
<asp:TemplateField HeaderText="Delete">
<ItemTemplate>
<asp:ImageButton ID="imgDelete" runat="server" CommandName="deletenotice" ImageUrl="~/images/delete1.gif" alt="Delete"
OnClientClick="return confirm('Are you sure want to delete the current record ?')">
</asp:ImageButton>
</ItemTemplate>
</asp:TemplateField>
C# Code
protected void gvNoticeBoardDetails_RowCommand(object sender, GridViewCommandEventArgs e)
{
if (e.CommandName.ToLower().Equals("deletenotice"))
{
GridViewRow row = (GridViewRow)(((ImageButton)e.CommandSource).NamingContainer);
NoticeBoard notice = new NoticeBoard();
HiddenField lblCust = (HiddenField)row.FindControl("hdngvMessageId");//Fetch the CourierId from the selected record
auditTrail.Action = DBAction.Delete;
Service simplyHRClient = new Service();
MessageClass messageClass = simplyHRClient.SaveNoticeBoard(notice, auditTrail);
if (messageClass.IsSuccess)
{
this.Page.AddValidationSummaryItem(messageClass.MessageText, "save");
showSummary.Style["display"] = string.Empty;
showSummary.Attributes["class"] = "success-message";
if (messageClass.RecordId != -1)
lblCust.Value = messageClass.RecordId.ToString();
}
else
{
this.Page.AddValidationSummaryItem(messageClass.MessageText, "save");
showSummary.Style["display"] = string.Empty;
showSummary.Attributes["class"] = "fail-message";
}
//Bind Again grid
GetAllNoticeBoard();
}
}
Hope it helps you

Generated ASP table add buttons that pull row values

ok so I have this ASP:Table which pulls its rows of info from a stored procedure query, only two columns but wish to add a third with a button for each row that when clicked will grab the information in the prior columns for use in another function.
not sure how the hell to code it though as the buttons are generated per row and how can I programme events for a unknown number of buttons there must be a way of doing It programmatically.
Table Header
Datacolumn1 Datacolumn2
a 2 Select
b 4 Select
e 9 Select
so when I press select on the second row it gives me string1=b string2=4
I imagine the code would look something like this (not likely but in a ideal world :D)
protected void select_row (eventargs as e)
{
string data1 = Row(e).cell(0).text
int data2 = Row(e).cell(1).text
}
For a grid like this
<asp:GridView ID="Grid1" runat="server" onrowcommand="Grid1_RowCommand" AutoGenerateColumns="false">
<Columns>
<asp:BoundField DataField="Datacolumn1" HeaderText="Data column1" />
<asp:BoundField DataField="Datacolumn2" HeaderText="Data column2" />
<asp:TemplateField>
<ItemTemplate>
<asp:Button runat="server" CommandName="SelectIt" Text="Select" CommandArgument='<%# Eval("Datacolumn1") %>' />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
Use RowCommand event
protected void Grid1_RowCommand(object sender, GridViewCommandEventArgs e)
{
var btn = (Button)e.CommandSource ;
if (e.CommandName == "SelectIt")
{
var Row =(GridViewRow) btn.NamingContainer;
var Datacolumn1 = Row.Cells[0].Text;
var Datacolumn2 = Row.Cells[1].Text;
btn.Text = "selected: " + Datacolumn1 + "," + Datacolumn2;
}
}

how to get row values when checkbox is checked in gridview

before I asked this I did some checking around to make sure that this doesn't turn out to be a duplicate and ways to get the row values from a row that has a checkbox in its template field...but I can't seem to get it working...So far I have tried
protected void Page_Load(object sender, EventArgs e)
{
Entities NW = new Entities();
var customers =
from c in NW.Customers
where (c.ContactName.StartsWith("Ma") || c.ContactName.StartsWith("An")
|| c.ContactName.StartsWith("T")
|| c.ContactName.StartsWith("V")
)
orderby c.ContactName ascending
select new
{
c.CustomerID,
c.ContactName,
c.CompanyName,
c.City
};
gv1.DataSource = customers.ToList();
gv1.DataBind();
}
protected void Button1_Click(object sender, EventArgs e)
{
foreach (GridViewRow row in gv1.Rows)
{
if (row.RowType == DataControlRowType.DataRow)
{
CheckBox chkRow = (row.Cells[0].FindControl("CB") as CheckBox);
if (chkRow.Checked)
{
Label1.Text = row.Cells[2].Text;
}
}
}
}
I have stepped through the button click event and when it gets to
if (chkRow.Checked)
its showing as null and skips over it..
my markup is
<asp:GridView ID="gv1" runat="server">
<HeaderStyle BackColor="SkyBlue" />
<AlternatingRowStyle BackColor="Yellow" />
<Columns>
<asp:TemplateField>
<ItemTemplate>
<asp:CheckBox ID="CB" runat="server" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
when I look at the source after I run, the checkboxes are all named differently than what I gave them the ID of "CB", attached is the pic of the source when its running
I am not sure what I am doing wrong with this
Your GridView should have more columns, because you are referencing Cell[2] in your code.
You might try to use row object to look for your control:
CheckBox chkRow = (row.FindControl("CB") as CheckBox);

Dynamic control Viewstate

I am now stuck. After spending ages trying to maintain dynamically added linkbutton or label to a gridview it seems I am overwriting a checkbox state on post back.
As stated I am dynamically adding either a linkbutton or a label to a place holder in a gridview with the following:
protected void LedgerGrid_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
int index = e.Row.RowIndex;
if (items[index].noteID > 0)
{
PlaceHolder label = (PlaceHolder)e.Row.FindControl("HasPrevious");
LinkButton link = new LinkButton();
link.CommandName = "LinkCommand";
link.Command += new CommandEventHandler(link_Command);
link.Text = "Yes";
link.ID = index.ToString();
label.Controls.Add(link);
}
else
{
PlaceHolder label = (PlaceHolder)e.Row.FindControl("HasPrevious");
Label noNote = new Label();
noNote.Text = "No";
label.Controls.Add(noNote);
}
Here is the gridview:
<asp:GridView runat="server" ID="LedgerGrid" AutoGenerateColumns="false"
onselectedindexchanged="LedgerGrid_SelectedIndexChanged"
onrowdatabound="LedgerGrid_RowDataBound" onrowcommand="LedgerGrid_RowCommand"
>
<Columns>
<asp:TemplateField HeaderText="Notes">
<ItemTemplate>
<asp:PlaceHolder ID="HasPrevious" runat="server"></asp:PlaceHolder>
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField HeaderText="Invoice Date" DataField="invoicedate" DataFormatString="{0:d}" />
<asp:BoundField HeaderText="Invoice" DataField="invoice" />
<asp:BoundField HeaderText="Fee Debt" DataField="Fee_Debt" DataFormatString="{0:C}" />
<asp:BoundField HeaderText="Cost Debt" DataField="Cost_Debt" DataFormatString="{0:C}" />
<asp:BoundField HeaderText="VAT Debt" DataField="VAT_Debt" DataFormatString="{0:C}" />
<asp:BoundField HeaderText="Total Debt" DataField="Total_Debt" DataFormatString="{0:C}" />
<asp:BoundField HeaderText="Client Code" DataField="ClientCode" />
<asp:ButtonField HeaderText="Matter Number" DataTextField="matternumber" CommandName="ViewMatter" />
<asp:BoundField HeaderText="Decription" DataField="matterdescription" />
<asp:TemplateField>
<ItemTemplate>
<asp:CheckBox ID="AddInvoiceCheck" runat="server" Enabled="true" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
To make sure that my link button command works I am rebuilding the gridview as I would in the Page_load. Here is the page load:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
string clientCode = Server.HtmlEncode(Request.QueryString["clientcode"]);
items = DataAccess.DataAccess.GetLedgerDetails(clientCode);
ViewState["LedgerItems"] = items;
ViewState["clientcode"] = clientCode;
LedgerGrid.DataSource = items;
LedgerGrid.DataBind();
LedgerClientObject clientDetails = DataAccess.DataAccess.GetClientDetails(clientCode);
ClientCodeLabel.Text = string.Format("Group Code: {0}", clientDetails.GroupCode);
ClientNameLabel.Text = string.Format("Client Name: {0}", clientDetails.ClientName);
ClientCodeFilter.DataSource = clientDetails.ClientCodes;
ClientCodeFilter.DataBind();
}
}
To maintain the dynamically added controls on post back I am calling the following:
protected void Page_PreLoad(object sender, EventArgs e)
{
if (IsPostBack)
{
items = (List<LedgerItem>)ViewState["LedgerItems"];
LedgerGrid.DataSource = items;
LedgerGrid.DataBind();
}
}
Of course it now seems that by using this method I am overwriting the state of the check-box gridview column. Am I approaching this all wrong? I would like to be able to use the viewstate to maintain the set up of my dynamically added controls to the gridview. Is there a way I can access the state of the checkbox control form gridview before/after I reload the gridview in PreLoad event?
I am checking the state of my checkbox column with a button click as follows:
protected void Unnamed1_Click(object sender, EventArgs e)
{
NoteModel note = new NoteModel();
for (int i = 0; i < LedgerGrid.Rows.Count; i++)
{
int invoice = Convert.ToInt32(LedgerGrid.Rows[i].Cells[2].Text);
CheckBox check = (CheckBox)LedgerGrid.Rows[i].FindControl("AddInvoiceCheck");
if (check.Checked)
{
note.invoices.Add(invoice);
}
}
if (note.invoices == null)
{
string clientcode = (string)ViewState["clientcode"];
ViewState["InvoiceError"] = 1;
Response.Redirect(string.Format("Ledger.aspx?clientcode={0}", clientcode));
}
else
{
string clientcode = (string)ViewState["clientcode"];
Session["NoteObject"] = note;
Response.Redirect(string.Format("AddNote.aspx?cc={0}", clientcode));
}
}
Whenever I iterate over the checkbox controls in the gridview they are all unchecked e.g overwritten by my PreLoad code!!!
In the end I decided not to use dynamically created controls. Instead I added a buttonfield to the grid view. In my rowdatabound event I then set the visibility of the button to false if it does meet my criteria. This way view state is maintained as i do not need to create control on post back.
Seems with webforms that avoiding dynamic controls where possible is the best bet. I would still like to know which event to tap into to override viewstate re-instating controls?

firing problem of selected_indexchanged(cant even print cell value in grid view by any event)

//code in aspx.
<Columns>
<asp:BoundField DataField="Name" HeaderText="Name" />
<asp:BoundField DataField="City" HeaderText="City" />
<asp:BoundField DataField="MobilePhone"
HeaderText="Mobile Phone" />
<asp:ButtonField Text="Button" ButtonType="Button" CommandName="Select" />
</Columns>
</asp:GridView>
<asp:Label ID="Label" runat="server" Text="Label"></asp:Label>
//code behind file()
protected void grid_SelectedIndexChanged(object sender,
EventArgs e)
{
int selectedRowIndex;
selectedRowIndex = grid.SelectedIndex;
GridViewRow row = grid.Rows[selectedRowIndex];
string name = row.Cells[0].Text;
Label.Text = "You selected " + name + ".";
}
Q: iam not even able to print the selected row.If anyone could help me with this issue.
You should really be getting this value from the underlying data item. But, if for some reason that value is being manipulated after it has been data-bound and you need to get the actual value of the control in that cell then you probably want something like this:
protected void grid_SelectedIndexChanged(object sender, EventArgs e)
{
GridViewRow row = (GridViewRow)grid.Rows[grid.SelectedIndex];
string name = ((Label)row.Cells[1].Controls[1]).Text;
Label.Text = "You selected " + name + ".";
}
but... that's a pretty messy way of achieving this. Also "Label" is a pretty bad name for a label. Also, Cell[0] like you posted is going to be the column with the select button most likely, not the column with the value you want. And, once you're in the correct cell, there are 3 controls, 2 literals and a label for a normal boundfield. Control[1], the second control, is the label control that will have the value you're looking for.
You have to use a RowCommand Event instead of a SelectedIndexChanged Event.
protected void GridView1_RowCommand(object sender, GridViewCommandEventArgs e)
{
if (e.CommandName == "Select")
{
GridViewRow row = ((GridViewRow)((Button)e.CommandSource).NamingContainer);
string name = row.Cells[0].Text;
Label.Text = "You selected " + name + ".";
}
}

Categories

Resources