ASP.net GridView InvalidOperationException on foreach when deleting - c#

I'm using ASP.net and i've added a delete button on a gridview and im trying to remove the row of the button clicked. The thing is though, that i get an InvalidOperationException on my foreach.
I may have other errors in the code or my logic may be wrong so if you see any of it please point them out. Thanks.
gridView cart.ASPX:
<asp:GridView ID="CartGrid" runat="server" AutoGenerateColumns="false" OnRowDeleting="RemoveSelected">
<Columns>
<asp:BoundField DataField="product_name" HeaderText="Name" />
<asp:BoundField DataField="price_per_unit" HeaderText="Price" />
<asp:BoundField DataField="unit" HeaderText="Unit" />
<asp:TemplateField>
<ItemTemplate>
<asp:LinkButton ID="delSelected" runat="server" Text="Delete" CommandName="Delete"></asp:LinkButton>
</ItemTemplate>
<ItemStyle Width="100px" />
</asp:TemplateField>
</Columns>
</asp:GridView>
delete method cart.ASPX.CS
protected void RemoveBtn(object sender, GridViewDeleteEventArgs e)
{
ArrayList cartList = (ArrayList)Session["cartList"];
GridViewRow row = (GridViewRow)CartGrid.Rows[e.RowIndex];
String name = row.Cells[0].Text;
//String name = (string)CartGrid.DataKeys[e.RowIndex].Value;
//String name = CartGrid.SelectedRow.Cells[1].Text;
foreach (product p in cartList)
{
if (p.product_name.Equals(name))
{
cartList.Remove(p);
}
}
CartGrid.DataBind();
}

Hi I think the issue is that you are using the cartlist for loop and at the same time you want to delete values from the same which is not possible .
just you need to change your approach like create the empty list and add all the index into it from where you have to delete the value Staring from bottom to top like from end of list so that it can not effect the index of the list by deleted value and after loop you can delete it using the new empty list which contain the list of all the values which should be deleted.

You can't modify the collection you're iterating over with the foreach - please see this question for more information.

Related

Set dynamic asp:HyperLinkField

I have a gridview that is filled when the page is loaded with a dataReader.
I can add a column with hyperlinks but I need the url to contain the GenCod that is dynamically added line by line to the gridView so each link sends to its detail page.
URL Example: http://localhost:50228/misesenventedetail.aspx?GenCod=9788416657544
How can i use the index or name of the column to get the GenCod?
<asp:GridView ID="aParaitre_GridView" runat="server" OnRowCommand="RowCommand" >
<Columns>
<asp:ButtonField buttontype="Image" CommandName="AjouterAuPanier" ImageUrl="~/Images/buy_this_logo.png" />
<asp:HyperLinkField NavigateUrl="url+Parameter+GENCOD" Text="VoirDetail" />
</Columns>
</asp:GridView>
This GridView contains much more columns.
Getting Error: System.Web.UI.WebControls.HyperLinkField has no DataBinding.
With the the following code:
<asp:HyperLinkField
NavigateUrl='<%# String.Format("~/misesenventedetail.aspx?GenCod={0}", Eval("GenCod"))%>'
Text="Detail"
runat="server"
/>
I was hoping something like this would work
<asp:HyperLinkField
id="MyLink"
NavigateUrl="~/misesenventedetail.aspx?GenCod<%= gencod %>"
runat="server" />
protected void RowCommand(object sender, GridViewCommandEventArgs e)
{
// Gets the index of the line (row) where we click
int index = Convert.ToInt32(e.CommandArgument);
GridViewRow ligneClick = aParaitre_GridView.Rows[index];
// Gets GenCod from column 1
var GenCod = ligneClick.Cells[1].Text;
MyLink.NavigateUrl = string.Format("../mypage.aspx?id={0}", GenCod );
}
SOLVED
I was able to resolve my problem with this:
<asp:HyperLinkField
DataNavigateUrlFields="GenCod" DataNavigateUrlFormatString="~/misesenventedetail.aspx?GenCod={0}" Text="Voir Details"
runat="server" />
StackOverflow is so great.

Retrieve values from GridView by changing a value in a DropDownList

I'm having problems retrieving the current row by changing the status of a DropDownList in the row. My code for the GridView is:
<asp:GridView ID="grdMappingList" runat="server" OnPageIndexChanging="grdMappingList_PageIndexChanging" AutoGenerateColumns="false" AllowPaging="true" PageSize="10" ShowHeaderWhenEmpty="true" UseAccessibleHeader="true" CssClass="table table-hover table-striped segment_list_tbl" >
<PagerStyle CssClass="grid_pagination" HorizontalAlign="Right" VerticalAlign="Middle" BackColor="#DFDFDF" />
<Columns>
<asp:BoundField HeaderText="Mapping Id" DataField="MAPPING_ID"></asp:BoundField>
<asp:BoundField HeaderText="PDM Name" DataField="PDM_NAME"></asp:BoundField>
<asp:BoundField HeaderText="PDM EntityID" DataField="PDM_ENTITY_ID" Visible="false"></asp:BoundField>
<asp:TemplateField HeaderText="Mapping Status" HeaderStyle-CssClass="width_120 aligned_center" ItemStyle-CssClass="width_120 aligned_center" Visible="true">
<ItemTemplate>
<asp:DropDownList id="ddlMappingStatus" runat="server" SelectedValue='<%# Bind("MappingCompleted") %>' OnSelectedIndexChanged="ddlMappingStatus_SelectedIndexChanged" AutoPostBack="true">
<asp:ListItem Text="Done" Value="DONE"></asp:ListItem>
<asp:ListItem Text="Not Done" Value="NOT DONE"></asp:ListItem>
</asp:DropDownList>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
And the code behind is:
protected void ddlMappingStatus_SelectedIndexChanged(object sender, EventArgs e)
{
int MappingID = Convert.ToInt16(grdMappingList.SelectedRow.Cells[0].Text);
DropDownList ddgvOpp = (DropDownList)grdMappingList.SelectedRow.FindControl("ddlMappingStatus");
if (ddgvOpp.Equals("DONE"))
{
}
}
I am getting an error on this line:
DropDownList ddgvOpp (DropDownList)grdMappingList.SelectedRow.FindControl("ddlMappingStatus");
and this line:
int MappingID = Convert.ToInt16(grdMappingList.SelectedRow.Cells[0].Text);
It seems that I can't retrieve the values! I dont know why. When I choose a new status from the DropDownList, I want to get all the values of the row in order to update the record.
Unfortunately, SelectedRow does not mean what you think it means. It doesn't just simply mean the current row you are working on or the current row with the controls that you are using. That property must get set somehow. This is usually done through a "Select" button on the GridView, such as an <asp:ButtonField> with the command name "Select".
You really do not need this however. You just need the row you are working with. Since you are using the SelectedIndexChanged event of your DropDownList, you already have what you need. You just need to go about it a different way.
First, get the DropDownList. Then get the row of the DropDownList. Now do whatever you need with the row.
But, from your code, it looks like you may not even need the row. You just need the value of the DropDownList. So you could skip that all together. But if you do need the row, here is how you would do that too.
protected void ddlMappingStatus_SelectedIndexChanged(object sender, EventArgs e)
{
DropDownList ddlMappingStatus = (DropDownList)sender;
if(ddlMappingStatus.SelectedItem.Text.ToUpper() == "DONE")
{
}
GridViewRow row = (GridViewRow)ddlMappingStatus.NamingContainer;
}
Now when you use <asp:BoundField> controls in your GridView, getting their values isn't very clean. You are forced to hardcode the column index like you were doing:
GridViewRow row = (GridViewRow)ddlMappingStatus.NamingContainer;
String mappingID = row.Cells[0].Text;
I tend to prefer using <asp:TemplateField> controls so I can use FindControl() to get what I am after. This prevents any reordering of the columns from breaking the code, as you'd have to hardcode different indexes. So for example, to find that DropDownList (since it already is a <asp:TemplateField>), you'd do something like this:
DropDownList ddlMappingStatus = (DropDownList)row.FindControl("ddlMappingStatus");

Retriving cell value from gridview rowcommand

hey I'm using to buttons in a gridview each which I want to assign to specific action involves retrieving certain cell values from the gridview columns
I tried using
GridViewRow row = (GridViewRow (((Button)e.CommandSource).NamingContainer);
but it gives me Unable to cast object of type 'System.Web.UI.WebControls.GridView' to type 'System.Web.UI.WebControls.Button'.
I also tried
int index = Convert.ToInt32(e.CommandArgument);
GridViewRow row = GridView1.Rows[index];
it gives me the following error
Specified argument was out of the range of valid values.
Parameter name: index
note that when I tried to specify the commandParameters property of the ButtonField the compiler said that it's not a valid parameter for the ButtomField
and I have 8 columns in the gridview so its not out of range
or alternatively could I use more than one select command button
if so how to say which one is clicked??
please help me im desperate
ASP code:
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" DataKeyNames="AcitivityId" DataSourceID="SqlDataSource1" Height="304px" Width="912px" OnRowCommand="GridView1_RowCommand">
<Columns>
<asp:BoundField DataField="AcitivityId" HeaderText="AcitivityId" InsertVisible="False" ReadOnly="True" SortExpression="AcitivityId" />
<asp:BoundField DataField="ActiviityName" HeaderText="ActiviityName" SortExpression="ActiviityName" />
<asp:BoundField DataField="ActivityLocation" HeaderText="ActivityLocation" SortExpression="ActivityLocation" />
<asp:BoundField DataField="ActivityStartDate" HeaderText="ActivityStartDate" SortExpression="ActivityStartDate" />
<asp:BoundField DataField="ActivityDueDate" HeaderText="ActivityDueDate" SortExpression="ActivityDueDate" />
<asp:BoundField DataField="ActivityDescription" HeaderText="ActivityDescription" SortExpression="ActivityDescription" />
<asp:ButtonField ButtonType="Button" CommandName="Avaliable" Text="Show avaliable buses" />
<asp:ButtonField ButtonType="Button" CommandName="Assigned" Text="Show assigned buses "/>
</Columns>
</asp:GridView>
I use this code in a number of places on my site to get the row from an object triggering a GridViewRowCommand
protected void btnUpdate_Command(object sender, CommandEventArgs e)
{
GridViewRow g = (GridViewRow)((Button)sender).NamingContainer;
// other stuff
}
Hope this helps.

Gridview Button in TemplateField only fires on second click

I've done some searching prior to asking, and although this post is close, it doesn't work for my scenario.
What I find is that the "delete" button in my template field seems to fire, but the click event does not trigger. Yet the second time you click the button it works as expected.
So, breaking the code down, I am binding my data to a GridView, using a `SqlDataSource.
My page load event starts as follows:
if (!Page.IsPostBack)
{
externalUserDataSource.ConnectionString = "some connection string";
}
My data source is as follows:
<asp:SqlDataSource ID="externalUserDataSource" runat="server"
ConflictDetection="CompareAllValues" SelectCommand="uspGetExternalUsersByTeam"
SelectCommandType="StoredProcedure" ProviderName="System.Data.SqlClient">
<SelectParameters>
<asp:SessionParameter Name="TeamID" SessionField="TeamID" Type="Int32" />
</SelectParameters>
</asp:SqlDataSource>
And this is my GridView markup:
<asp:GridView ID="gridView" runat="server" AutoGenerateColumns="False"
BackColor="White" BorderColor="#3366CC" BorderStyle="None" BorderWidth="1px"
CellPadding="4" DataKeyNames="LoginID" DataSourceID="externalUserDataSource"
EnableModelValidation="True" OnRowDataBound="GridViewRowDataBound" TabIndex="3">
<HeaderStyle BackColor="#003399" Font-Bold="True" ForeColor="White" />
<FooterStyle BackColor="#99CCCC" ForeColor="#003399" />
<PagerStyle BackColor="#99CCCC" ForeColor="#003399" HorizontalAlign="Left" />
<RowStyle BackColor="LightGoldenrodYellow" />
<SelectedRowStyle BackColor="#009999" Font-Bold="True" ForeColor="#CCFF99" />
<Columns>
<asp:BoundField DataField="RowID" HeaderText="Row ID" ReadOnly="True"
SortExpression="RowID" Visible="False" />
<asp:BoundField DataField="LoginID" HeaderText="Login ID" ReadOnly="True"
SortExpression="LoginID" Visible="False" />
<asp:BoundField DataField="EmailAddress" HeaderText="Email Address"
ItemStyle-VerticalAlign="Bottom" ReadOnly="True" SortExpression="AssociateName"/>
<asp:BoundField DataField="TeamID" HeaderText="Team ID" ReadOnly="True"
SortExpression="TeamID" Visible="False" />
<asp:CheckBoxField DataField="HasFIAccess"
HeaderText="Has Access to<br />Funding<br/>Illustrator"
ItemStyle-HorizontalAlign="Center" ItemStyle-VerticalAlign="Bottom"
ReadOnly="True"/>
<asp:CheckBoxField DataField="HasALTAccess"
HeaderText="Has Access to<br />Asset Liability<br/>Tracker"
ItemStyle-HorizontalAlign="Center" ItemStyle-VerticalAlign="Bottom"
ReadOnly="True"/>
<asp:CheckBoxField DataField="HasFIAAccess"
HeaderText="Has Access to<br />Funding<br/>Illustrator App"
ItemStyle-HorizontalAlign="Center" ItemStyle-VerticalAlign="Bottom"
ReadOnly="True"/>
<asp:TemplateField>
<ItemTemplate>
<asp:Button runat="server" CssClass="additionsRow" ID="btnDeleteExternalUser" OnClick="DeleteExtUserButtonClick"
CausesValidation="False" Text="Delete"
CommandArgument='<%#Eval("TeamID") + "," + Eval("LoginID") + "," + Eval("EmailAddress") + "," + Eval("HasALTAccess")%>'/>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
So, you can see that I am passing across some information in the button that is used in the event to ensure the correct data is deleted (which is why I cannot use the ButtonField, as suggested in the link above).
The last part to the puzzle is the GridView's databound event:
protected void GridViewRowDataBound(object sender,
GridViewRowEventArgs e)
{
// if rowtype is not data row...
if (e.Row.RowType != DataControlRowType.DataRow)
{
// exit with no further processing...
return;
}
// get the ID for the selected record...
var selectedId = DataBinder.Eval(e.Row.DataItem, "RowID").ToString();
// create unique row ID...
e.Row.ID = string.Format("ExtUserRow{0}", selectedId);
// find the button delete for the selected row...
var deleteButton = (Button)e.Row.FindControl("btnDeleteExtUser");
// get the email address for the selected record...
var selectedUser = DataBinder.Eval(e.Row.DataItem, "EmailAddress").ToString();
// define the message text...
var messageText = string.Format("OK to delete {0}?",
selectedUser.Replace("'", "\\'"));
// add attribute to row delete action...
this.AddConfirmMessage(deleteButton, messageText);
}
Where AddConfirmMessage simply assigns an onclick attribute to the control to ensure the user has to confirm the deletion.
Now, in every case the message pops up 'OK to delete abc#xyz.com?', but as stated earlier, the event assigned to the "delete" button does not trigger until the button is clicked a second time.
Strangely enough, I took this code from another page and modified accordingly, though it doesn't have this issue there:
protected void DeleteExtUserButtonClick(object sender,
EventArgs e)
{
// get the buton which was clicked...
var button = (Button)sender;
// break the delimited array up...
string[] argumentArray = button.CommandArgument.Split(',');
// store the items from the array...
string teamId = argumentArray[0];
string loginId = argumentArray[1];
string emailAddress = argumentArray[2];
string hasAltAccess = argumentArray[3];
using (var conn = new SqlConnection(Utils.GetConnectionString()))
{
// create database command...
using (var cmd = new SqlCommand())
{
// set the command type...
cmd.CommandType = CommandType.StoredProcedure;
// set the name of the stored procedure to call...
cmd.CommandText = "uspDeleteExternalUser";
// create and add parameter to the collection...
cmd.Parameters.Add(new SqlParameter("#TeamId", SqlDbType.Int));
// assign the search value to the parameter...
cmd.Parameters["#TeamId"].Value = teamId;
// create and add parameter to the collection...
cmd.Parameters.Add(new SqlParameter("#LoginId", SqlDbType.VarChar, 50));
// assign the search value to the parameter...
cmd.Parameters["#LoginId"].Value = loginId;
// set the command connection...
cmd.Connection = conn;
// open the connection...
conn.Open();
// perform deletion of user...
cmd.ExecuteNonQuery();
}
}
// bind control to refresh content...
ExtUsersGrid.DataBind();
}
Have I missed something obvious? I am happy to modify if there are better ways to do this.
Edit 1: Following on from the discussions below, I have modified the following:
Removed the Onclick event property of the ButtonItem;
Set the CommandName and CommandArgument as suggested below, and updated the DataKeyNames to use RowID which is a unique ID from the data;
Assigned a RowCommand event for the GridView;
Assigned the delete code to the RowCommand event.
Following these changes, it still fires the row event code on the second click.
Edit 2: FYI - I've now removed the SqlDataSource and the associated code/references, and created a procedure to fill the dataset, which is called on Page_Load (inside the !Page.IsPostBack brackets). I started making the changes below to use the RowCommand event, but they still caused the same issue (i.e. the button will only fire on the second click). As using RowCommand meant converting the BoundFields to ItemTemplates, I reverted back to the button click event as it seemed pointless making all those changes for no gain. If anyone else can help me understand why it only triggers on the second click, would appreciate your input.
OK, frustratingly this was due to some code that for reasons still unknown, works elsewhere.
In the DataBound event, there was two lines of code:
// get the associate name for the selected record...
var selectedId = DataBinder.Eval(e.Row.DataItem, "RowID").ToString();
// create unique row ID...
e.Row.ID = string.Format("ExtUserRow{0}", selectedId);
The process of applying an ID to the rows programatically seems to break the connection between the data and the events.
By removing these two lines of code, it works as expected.
Well instead you can do something like this.
Add a CommandName property to your GridView like this. Also note the changes in the CommandArgument property:
<asp:TemplateField>
<ItemTemplate>
<asp:Button runat="server" CssClass="additionsRow" ID="btnDeleteExtUser"
CausesValidation="False" Text="Delete"
CommandName="OnDelete"
CommandArgument='<%# Container.DisplayIndex %>" '
</ItemTemplate>
</asp:TemplateField>
Code behind will look something like this. Note that I am using the RowCommand event of Gridview.
protected void GridView1_RowCommand(object sender, GridViewCommandEventArgs e)
{
if(e.CommandName == "OnDelete")
{
int rowIndex = Convert.ToInt32(e.CommandArgument);
// now you got the rowindex, write your deleting logic here.
int ID = Convert.ToInt32(myGrid.DataKeys[rowIndex]["ID"].Value);
// id will return you the id of the row you want to delete
TextBox tb = (TextBox)GridView1.Rows[rowIndex].FindControl("textboxid");
string text = tb.Text;
// This way you can find and fetch the properties of controls inside a `GridView`. Just that it should be within `ItemTemplate`.
}
}
Note: mention DataKeyNames="ID" inside your GridView. The "ID" is the primary key column of your table.
Are you binding the GridView on pageload? If so, then move it to !IsPostBack block as show below:
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
GridView1.DataSource = yourDataSource;
GridView1.DataBind();
}
}

VS2010 asp.net : capturing an invisible GridView column value

How do I pass a GridView column value to the code behind with out making it visible? In the following code, I would like to capture the ContactID with out making it visible in the GridView.
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
DataSourceID="SqlDataSource1"
OnRowCommand="_OnRowCommand"
>
<Columns>
<asp:BoundField DataField="ContactID" HeaderText="ContactID"
SortExpression="ContactID" InsertVisible="False" ReadOnly="True" />
.....other columns in the grid....
<asp:ButtonField ButtonType=Button CommandName="Remove" Text="Remove" />
</Columns>
</asp:GridView>
I would like to capture ContactID as follows. I can not do so with out making it visible = true in the GridView.
if (e.CommandName.Equals("Remove"))
{
int index = Convert.ToInt32(e.CommandArgument);
int ContactID = Convert.ToInt32(GridView1.Rows[index].Cells[0].Text.Trim());
}
Please help.
Thanks..
What you want and what your code is doing are two different things.
I think what you are looking for is this:
String ContactID = e.Row.Cells(0).Text;
However, I propose a different way:
You can use a CommandArgument on your Button. That means that you are passing the argument through the Button itself. You won't need to get the argument from any other control. To do that, you would do this :
<asp:ButtonField ButtonType=Button CommandName="Remove" Text="Remove" CommandArgument='<%# Eval("ContactID") %>' />
And then your code looks like this:
if (e.CommandName.Equals("Remove"))
{
int ContactID = Convert.ToInt32(e.CommandArgument);
}
Here's a good example of GridView with CommandArguments.

Categories