ASP.NET GridView requires two clicks of edit command - why? - c#

Firstly, I realise the question of how to avoid two clicks has been asked multiple times with ths same answer - "re-bind the gridview" (eg here and here). However I'm after an explanation of WHY this occurs and if there's an alternative solution.
When using a Gridview, clicking the Edit button once fires the OnRowEditing event, yet makes no visual changes to the gridview, upon clicking for a second time, the OnRowEditing executes again, but this time on return the GridView row is now editable and has the Update and Cancel buttons.
If within the OnRowEditing method there is a manual DataBind() of the gridview, then a second click is not required.
Why is this occuring?
So, the scenario:
GridView:
<asp:GridView ID="gvMain" runat="server"
OnRowEditing="gvMain_RowEditing"
OnRowCancelingEdit="gvMain_RowCancelingEdit"
OnRowUpdating="gvMain_RowUpdating">
<Columns>
<asp:CommandField ShowEditButton="true" EditText="Edit" />
<asp:BoundField DataField="location" HeaderText="Location" />
</Columns>
</asp:GridView>
CodeBedhind:
protected void gvMain_RowEditing(object sender, GridViewEditEventArgs e)
{
gvMain.EditIndex = i;
}

Related

How do I get the row I clicked with a button

I have a grid view and when I click on the button I wanted to do a SQL command to see which Order number was pressed. How do I get the row I have clicked on?
asp
<asp:GridView ID="GridView1" runat="server"
DataKeyNames="No_" AutoGenerateColumns="false" style="color:Black;border-collapse:collapse;margin-right: auto;display: table;text-align: center;" OnPageIndexChanging="MyGrid_PageIndexChanging" OnRowDataBound="MyGrid_RowDataBound" AllowPaging="True" PageSize="20" AllowCustomPaging="False" >
<Columns>
<asp:BoundField DataField="No_" HeaderText="No_Encomenda" />
<asp:BoundField DataField="[Item No_]" HeaderText="Item number" />
<asp:BoundField DataField="Quantity" HeaderText="Quantity" />
<asp:TemplateField HeaderText="Select">
<ItemTemplate>
<asp:CheckBox ID="CheckBox1" runat="server" OnCheckedChanged="CheckBox1_CheckedChanged" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
<PagerSettings Mode="NumericFirstLast" PageButtonCount="4" FirstPageText="First" LastPageText="Last"/>
<PagerStyle CssClass="gridview" HorizontalAlign="Center" VerticalAlign="Middle"/>
</asp:GridView>
cs
protected void ButtonDetails_Click(object sender, EventArgs e)
{
MyGrid.Visible = false;
GridView1.Visible = true;
}
Gridview image:
Well, first of all, in your markup, I don't see a button in your markup. So the picture you have, and the markup don't agree with each other.
There are about 3 great ways to get the row click with a button.
Most common:
Drop in a the button. Say like this:
<asp:TemplateField HeaderText="View">
<ItemTemplate>
<asp:Button ID="cmdView" runat="server" Text="Details" CommandName="Select" />
</ItemTemplate>
</asp:TemplateField>
Note how we added the CommandName = "select". This is a special option that will trigger the Gridivew RowIndexed changed. It ALSO triggers the row command. But, in row command the "selected row event" has not yet triggered. So, I just ignore the row command event, and put your code in the selected index changed event.
protected void MyGrid_SelectedIndexChanged(object sender, EventArgs e)
{
// Get selected row
GridViewRow dG = MyGrid.SelectedRow;
Response.Write("Row index was " + dG.RowIndex.ToString());
// get non templated columns - they appear in cells
Response.Write("2nd colum value = " + dG.Cells[1].Text);
// get templated fields - say a combo box for city
DropDownList CityComboBox = (DropDownList)dG.FindControl("DropDownList1");
Response.Write("City selected from combo box = " + CityComboBox.SelectedValue);
}
So by JUST adding the CommandName = "Select", then this will trigger the SelectedinddexChanged event.
Another way is to use the row command (but you have to pass the row index to that event).
But, a really slick way is to 100% ignore the grid events, and JUST use your button.
You can do it this way:
Drop in the button. But now you can't double click on the button to wire up the click event, but you CAN STILL set the event for the click.
While in the markup you can thus type in OnClick= NOTE VERY careful the intli-sense that pops up - it looks like this:
So in the above choices - choose the create new event - it SEEMS like nothing occurred, but when you flip back to code-behind, you have a nice simple clean, good old regular button click event.
Droop a button - create click event. You can now just 100% ignore the complex GridView events (and that select command etc.).
You do this way now:
protected void Button3_Click(object sender, EventArgs e)
{
Button MyButton = (Button)sender;
GridViewRow dG = (GridViewRow)MyButton.Parent.Parent;
Response.Write("Row index was " + dG.RowIndex.ToString());
// get non templated columns - they appear in cells
Response.Write("2nd colum value = " + dG.Cells[1].Text);
// get templated fields - say a combo box for city
DropDownList CityComboBox = (DropDownList)dG.FindControl("DropDownList1");
Response.Write("City selected from combo box = " + CityComboBox.SelectedValue);
}
I find the above is less hassle, but also LESS learning curve to use. We drop in a button - click event. You just pick up the "sender" into a button, and then get parent.parent which turns out to be the grid row we want.
The first parent is I think some cell divider. In fact I use this .parent trick all the time. Thus buttons are a simple button dropped into the grid markup, and we use the standard button click event and code approach.
But, hey, we really don't care about the GridVieew row command, and we really dont' care about the Selected index changed.
We have a button click. So now you can use this slick trick to save world poverty's, save using complex GridView events, and just code up a simple button like any other button.
And we get FULL USE of the grid view row with this trick.

Loading Gridview

I have a gridview that is populated from the code behind, and has about 300 rows. When I try to access the page containing it, everything loads, and then about 5 seconds later, the program quits and this error message appears:
If I press continue, the application stops running. However, when I look at the page, all of the data has loaded into the gridview (but of course my links, etc, don't work because the session has stopped running).
If I put less data in the table that populates the gridview, I do not get an error (it works with about 30 rows--I'm not sure the exact point where it becomes too much data). Anyway, since it is the exact same code but just less data, I know that I don't actually have an infinite loop or infinite recursion like the message suggests.
Here is the html for the gridview:
<div id="dvGrid" class="gridTable">
<asp:GridView runat="server" ID="GridView1" OnRowDataBound="GridView1_RowDataBound">
<Columns>
<asp:BoundField DataField="Edit" HtmlEncode="false" HeaderText="" HeaderStyle-Wrap="false" SortExpression="Edit" />
</Columns>
</asp:GridView>
</div>
Here is where it is populated in the code behind (this is in the Page_Load method):
DataTable dt = OpenWeather10Day.DBQueries.GetHistoricalData(_Zip);
dt.Columns["Date"].SetOrdinal(0);
GridView1.DataSource = dt;
_LinkColIndex = dt.Columns["Edit"].Ordinal;
_CommentsColIndex = dt.Columns["Comments"].Ordinal;
GridView1.DataSource = dt.DefaultView;
GridView1.DataBind();
And here is the OnRowDataBound function:
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{//remove double edit column and add a div in comments column to make it the correct size
TableCell cell = e.Row.Cells[0];
e.Row.Cells.RemoveAt(0);
e.Row.Cells.RemoveAt(_LinkColIndex);
e.Row.Cells.Add(cell);
if (e.Row.RowType == DataControlRowType.DataRow) {
TableCell commentsCell = e.Row.Cells[_CommentsColIndex];
HtmlGenericControl div = new HtmlGenericControl("DIV");
div.Attributes.Add("class", "innerDiv");
div.InnerHtml = commentsCell.Text;
commentsCell.Text = string.Empty;
commentsCell.Controls.Add(div);
}
}
I have discovered that the error is with my "edit" column. If I delete the edit column from the table and get rid of all of the code related to it, all 300 rows load with no problem and the page is still active. The problem is that the edit column is critical to my page, so that is not a possible solution.
I've looked into pagination on scroll, but I can't find an example/demo that does exactly what I need or is simple enough for me to follow (I'm pretty much a complete beginner). I also don't think that it should be necessary to implement pagination; it's okay if the page takes a few seconds to load. My real problem/question is with the stack overflow that causes the session to quit. I have no idea what it is about the edit column that is causing this to occur, but somehow I need to fix this so that accessing this page doesn't quit the entire session. I am willing to add pagination on scroll if it is the only option, but like I said, I haven't been able to figure it out yet. I'm also not sure that it would fix the problem. I'm happy to post any other code, etc, if it'd be at all helpful!
Can you explain what is in your Edit column? Is it a Url, preformatted HTML? What will it do?
You are seeing 2 Edit columns because you have one added in your GridView, then you are adding a second one when you bind your data. Use the AutoGenerateColumns property to prevent this, then add in your other columns.
Example
<asp:GridView runat="server" ID="GridView1"
AutoGenerateColumns="false">
<Columns>
<asp:BoundField DataField="Edit" HeaderText="Edit" />
<asp:BoundField DataField="Date" HeaderText="Date" />
<asp:TemplateField HeaderText="Comments">
<ItemTemplate>
<div class="innerDiv">
<%# Eval("Comments") %>
</div>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
You can use a TemplateField to put in the HTML in to your Comments column and get rid of
OnRowDataBound="GridView1_RowDataBound"

Form Re-Submission Deletes Next GridView Row

I have a Gridview and the OnRowDeleting method with removes a record from the database and the gridview.
My problem is that when a user click the delete button in the Gridview the record will be removed from the database but the GridView does not show this update (does not refresh). If the user then refreshes the page causing a form re-submission then the GridView deletes the row below the originally deleted row and so on if the page is refreshed again.
How can I prevent this from happening?
Here is my gridview:
<asp:GridView runat="server" ID="gvShowQuestionnaires" HeaderStyle-CssClass="table_header" CssClass="view" AlternatingRowStyle-CssClass="alt" AlternatingRowStyle-BackColor="#f3f4f8" AutoGenerateColumns="False"
DataKeyNames='QuestionnaireID' OnRowDeleting="gvShowQuestionnaires_RowDeleting" OnRowEditing="gvShowQuestionnaires_RowEdit" OnSelectedIndexChanged="gvShowQuestionnaires_SelectedIndexChanged" FooterStyle-CssClass="view_table_footer" >
<Columns>
<asp:BoundField DataField="QuestionnaireID" HeaderText="ID" HeaderStyle-Width="80px" ItemStyle-CssClass="bo"></asp:BoundField>
<asp:BoundField DataField="QuestionnaireName" HeaderText="Questionnaire Name" />
<asp:ButtonField CommandName="select" ButtonType="Link" Text="hello" />
<asp:CommandField HeaderText="Options" CausesValidation="true" ShowDeleteButton="True" ShowEditButton="true" EditText="Edit">
</asp:CommandField>
</Columns>
</asp:GridView>
Here is the OnRowDeleting method.
protected void gvShowQuestionnaires_RowDeleting(object sender, GridViewDeleteEventArgs e)
{
int questionnaireID = (int)gvShowQuestionnaires.DataKeys[Convert.ToInt32(e.RowIndex)].Value;
GetData.DeleteQuestionnaire(questionnaireID); // Deletes Questionniare from database
gvShowQuestionnaires.DataSource = DT;
gvShowQuestionnaires.DataBind();
}
You could always redirect the user to the initial page after the row was deleted, e.g. in the RowDeleted handler.
Response.Redirect(Request.RawUrl);
This should work because the delete action is a POST command to the current .aspx page.

Binding a Button to a GridView

This a pretty simple question, I'm just not sure how to do it exactly. I would like to bind a Button or perhaps ImageButton to a GridView in ASP.NET/C#. Currently, the GridView has two columns and is bound to a DataTable with two columns. I want to add a third column to the GridView, which will include the Button.
I know GridView has ButtonField, but I'm not too sure how to go about using it to do what I want. I want to dynamically generate these Buttons and add them to the GridView.
Here is how my GridView looks right now:
<asp:GridView
ID="GridView1"
Runat="server">
<Columns>
<asp:HyperLinkField
HeaderText="Display Name"
DataNavigateUrlFields="DISPNAME"
DataNavigateUrlFormatString="ViewItem.aspx"
DataTextField="DISPNAME">
<ItemStyle Width="70%" />
</asp:HyperLinkField>
<asp:BoundField
DataField="TypeDisp"
HeaderText="Type">
<ItemStyle Width="20%" />
</asp:BoundField>
</Columns>
</asp:GridView>
You can use a template field like the following,
<TemplateField>
<ItemTemplate>
<asp:ImageButton ImageUrl="image url" CommandName="SomeCommand" CommandArgument='<%# Eval("Id") %>'/>
</ItemTemplate>
</TemplateField>
Then you can handle the RowCommand event of the GridView and check the e.CommandName to see what command to be executed and you can get the e.CommandArgument as well which could be the row Id like I used in the code above.
If we are talking a button that's always present, you can use ButtonField, or even use a TemplateField and provide the template with the button, and bind the data to the button (sounds like you may want to bind data to the attributes of the button?)
If you are looking to dynamically generate buttons in the UI, tap into the RowCreated event and add the button the GridView. You'd have to do this on every page load; the GridView won't remember a button created programmatically.
HTH.

Checked checkbox in gridview, in c# codebehind checked property is false

I have custom upload control. The control has gridview with the uploaded documents and the first column has checkbox for selecting documents I want to delete by clicking Delete button. The upload control is contained in other UserControls almost everywhere in the application and works properly, except at one control. The problem is when I check some document for deleting, when the code executes the checked property of the checkbox is false.
<asp:GridView ID="gvFiles" runat="server" AutoGenerateColumns="false" ShowHeader="true"
CssClass="DataGrid" Width="100%" OnRowDataBound="gvFiles_RowDataBound">
<HeaderStyle HorizontalAlign="left" CssClass="UploadControlGridHeader" />
<RowStyle CssClass="dlcell" />
<EditRowStyle CssClass="dlsell" />
<Columns>
<asp:TemplateField HeaderText="Delete">
<ItemStyle Width="8%" HorizontalAlign="Center" />
<ItemTemplate>
<asp:CheckBox ID="chkFile" runat="server" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
and this is the delete button event
protected void btnDelete_Click(object sender, EventArgs e)
{
for (int i = 0; i < gvFiles.Rows.Count; i++)
{
CheckBox chk = (CheckBox)gvFiles.Rows[i].Cells[0].FindControl("chkFile");
if (chk.Checked)
{
// If file is marked/checked to be deleted
// then delete it.
DeleteFile(i);
}
}
Session[KEY_VIEWSTATEFILES] = this.FileList;
// Repopulate gridview
BindToGridView();
// Call command to keep same visible screen.
CustomCommandEventArgs command = new CustomCommandEventArgs(COMMAND_FILE_DELETED, null);
OnHappyCommand(command);
}
Not having your DeleteFile(i) code, I can't be sure, but I will take a guess that into that function you remove/delete the row. Am I right? If yes, then it change the index of all your rows, which make you think it is not checked when you check the row that you remember to have checked. You will have to use a backward for loop to fix this.

Categories