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"
Related
I'm working on an old site that originated as a Visual Studio Website project, and which I'm converting to a Web Application in addition to other work. One page on the site uses a GridView, and sets the DataSource in code-behind using a DataTable. The GridView exposes a couple of BoundFields, as well as one TemplateField that has a checkbox in it. The GridView is configured to use EnableViewState.
<asp:GridView ID="Results" runat="server" AutoGenerateColumns="False" CellPadding="4" ForeColor="#333333" GridLines="None" EnableViewState="true">
<AlternatingRowStyle BackColor="White" ForeColor="#284775" />
<Columns>
<asp:BoundField DataField="Type" HeaderText="Type"/>
<asp:BoundField DataField="ProcessDate" HeaderText="Process Date" />
<asp:BoundField DataField="Classification" HeaderText="Classification" />
<asp:BoundField DataField="Email" HeaderText="Email" />
<asp:TemplateField HeaderText="Notify?" ItemStyle-HorizontalAlign="Center">
<EditItemTemplate>
<asp:CheckBox ID="CheckBox1" runat="server" onclick="EnableSubmit(this);" />
</EditItemTemplate>
<ItemTemplate>
<asp:CheckBox ID="CheckBox1" runat="server" onclick="EnableSubmit(this);" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
The page also has a Submit button, and in the _Click handler, the code attempts to grab Results.Rows, and then digs around in order to determine which rows have their checkboxes checked. It does its subsequent processing based on the checked rows.
The old site code does IsPostBack checking, and only calls DataBind when it's false.
The page doesn't define either EnableViewState or ViewStateMode, which means they're both defaulting to true, according to MSDN.
There's no call to DataBind in a master page or anything.
The old site code does not do any manual persisting to the ViewState dictionary.
Amazingly, the production version of this site actually works.
Since I've been working on this, I've found only threads and articles discussing how GridView doesn't actually use ViewState, and how in order to use data sent to the user, the DataTable used as the DataSource must be manually inserted into ViewState in the code behind, etc.
The version of the site I'm working on has been converted to a Web Application project, and it's targeting .NET 4.5. When I debug the site and break in Page_Load or Submit_Click, the Results GridView has a null DataSource and the Rows property has 0 count. This seems to agree with the current conventional wisdom concerning how GridView "works."
I'm well aware that for what this site is doing, this is a heinous implementation, and there are much better ways to do it. However, what I'm most concerned about is that I can't find any explanation as to how the old version is working.
Did GridView change at some point in its history to ignore its EnableViewState property? What does EnableViewState on GridView actually do? Could it be a difference between Website and Web Application projects?
How can this possibly be working?
UPDATE: Based on Bert Evans' example page, I tried out this modified page.
<%# Page Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
public class Thing
{
public int ID { get; set; }
public string Name { get; set; }
}
IEnumerable<Thing> Things
{
get
{
var things = new List<Thing>();
things.Add(new Thing { ID = 1, Name = "One" });
things.Add(new Thing { ID = 2, Name = "Two" });
things.Add(new Thing { ID = 3, Name = "Three" });
things.Add(new Thing { ID = 4, Name = "Four" });
things.Add(new Thing { ID = 5, Name = "Five" });
return things;
}
}
void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
ThingGridView.DataSource = Things;
ThingGridView.DataBind();
}
pageLoadLabel.Text = string.Format("On page Load, row count: '{0}'", ThingGridView.Rows.Count);
}
void OnClickMe(object sender, EventArgs e)
{
onClickLabel.Text = string.Format("On click, row count: '{0}'", ThingGridView.Rows.Count);
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:GridView runat="server" ID="ThingGridView">
<Columns>
<asp:BoundField HeaderText="Name" DataField="Name" />
</Columns>
</asp:GridView>
</div>
<asp:Button runat="server" ID="ThingButton" Text="Click Me!" OnClick="OnClickMe" />
<asp:Label runat="server" ID="onClickLabel" Text="[set on click]" />
<asp:Label runat="server" ID="pageLoadLabel" Text="[set on page load]" />
</form>
</body>
</html>
The only differences are the labels that print out the row count in the page body. When executing this page. the first Page_Load call prints row count 5 to the page; however, in postback, the row count is 0 in both page load and the OnClick method.
Databinding is the process of translating the specified datasource for a control into a control tree that is rendered to the page. For GridViews, essentially, setting the DataSource property and calling the DataBind method translates an IEnumerable, or an IEnumerable extracted from another source like a DataSet or DataTable, into an HTML table with controls containing the data.
Each of the controls rendered into the table cells maintains it's own ViewState, so that when the page is posted back to the server, the GridView rebuilds the control structure, and populates the controls with the data in the ViewState (or rather, the Page initiates the rebuilding of the control structure and the GridView just participates).
The DataSource is not saved to the ViewState, only the state of the rendered controls.
Disabling the ViewState on a GridView prevents it from saving certain elements of it's own state, which will prevent it from being able to do things like pagination and sorting. Furthermore, if you disable the ViewState on the GridView, and you perform a postback (trigger an event on the client side that submits the page back to the server), then the GridView will show nothing when the page is re-rendered, because EnableViewState is inherited and the child controls will be prevented from saving their own state. The only way to get a GridView with a disabled ViewState to show data again after a post back would be to either re bind the data (call DataBind again with a data source that has data) or to have manually enabled ViewState on the child controls contained inside the GridView with the disabled ViewState. You mentioned that the DataBind on the page you are working on is guarded with !IsPostback, so it is only binding on the initial load of the page.
I don't know of anything changing such that the GridView at one point in time saved the DataSource, I believe this is how it always worked.
Thanks to Bert Evans and his example page I eventually isolated the problem. The bug is actually in the remarkably poor MSDN code example for UnityHttpModule, which integrates Unity DI with ASP.NET. UnityHttpModule detailed at https://msdn.microsoft.com/en-us/library/ff664534(v=pandp.50).aspx.
In addition to not actually even compiling as listed, the class wires up the DI code on the InitComplete event, which takes place before ViewState is loaded, as described in this article: ASP.NET 4.0 GridView OnRowEditing events not firing when using Unity 2.0 http module. In my case, moving the DI code to execute on PreLoad fixed my problem.
Finally, for completeness, and for anyone else having problems with the MSDN Unity DI HttpModule, a different SO thread also provides a working example: ASP.NET Dependency Injection HTTP Module (MS Enterprise Library).
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;
}
I have been coming across a strange bug/error in my code when attempting to select and pull data from multiple rows in GridView controls.
Background:
In short, I have a GridView item, populated with rows of different equipment items that are "checked in" and available to check out of our system. I want to have the user able to select multiple items and check them out at once, rather than doing it X times.
I am using a check box item on each row to do this, like so: http://i.imgur.com/fPYV2.png
There is a button at the bottom to check the equipment out of our database.
Code:
The code I am using to generate the ASPX page is:
<asp:GridView ID="grdEquipment" runat="server" AutoGenerateColumns="false" ShowHeaderWhenEmpty="true" CssClass="table table-bordered">
<Columns>
<asp:TemplateField HeaderText="Select">
<ItemTemplate>
<asp:CheckBox ID="chkMultiSelect_Out" runat="server" />
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField HeaderText="Equipment Name" DataField="Name" />
<asp:BoundField HeaderText="Category" DataField="Category.Name" />
</Columns>
<EmptyDataTemplate>
There is no equipment to display.
</EmptyDataTemplate>
</asp:GridView>
<div class="well">
<asp:Button ID="btnCheckOut" runat="server" CssClass="btn btn-small btn-inverse" Text="Check Out" OnClick="btnCheckOut_Click" />
</div>
This works and compiles with no problem.
The code running the button to check everything out is:
protected void btnCheckOut_Click(object sender, EventArgs e)
{
int checkoutNumber = 0;
//string id = (sender as Button).Attributes["data-id"].ToString();
SortedBindingList<Tracker.Business.Equipment> eqList = Tracker.Business.EquipmentList.FetchAll();
foreach (GridViewRow row in this.grdEquipment.Rows)
{
CheckBox chkMultiSelect_Out = (CheckBox)row.FindControl("chkMultiSelect_Out") ;
if (chkMultiSelect_Out.Checked)
{
checkoutNumber++;
Tracker.Business.Equipment equip = Tracker.Business.Equipment.GetByIdentification(Int32.Parse(row.ID.ToString()));
eqList.Add(equip);
}
}
//If checkoutNumber is 0, do nothing.
//If checkoutNumber is 1, launch popup for only 1 item
//If checkoutNumber is more than 1, launch popup for multiple items.
if (checkoutNumber == 0)
{
Response.Redirect("~/Equipment/CheckInOut/Default.aspx");
}
else if (checkoutNumber == 1)
{
}
else if (checkoutNumber > 1)
{
}
}
Now, OBVIOUSLY, this code isn't finished. I am debugging as I go to make my life easier.
The important code is the foreach loop where I check each row for the control item, and see if it is checked or not.
The Problem:
When I run the code, and test with some check boxes checked, it looks at all the rows, and ALL of them have "Checked = false" as an attribute. What's worse, I found out later that it doesn't even grab the data from the row, as shown in this screen shot here: http://i.imgur.com/clRuk.png
The text should be "Optimus Prime" or "Switch #1", however it is not! And both are checked when I run the code, yet the code sees only false checked items.
The code runs through the foreach loop for each row, i.e., 3 rows, it runs through the loop 3 times. So it sees each row, yet it is not pulling the data...
Where should I start looking?
I fixed my problem. As Tim Schmelter said, I was postbacking the page on every click, so I changed my code to databind the gridview table only on a page load, and not on a post back. Works now!
I've read multiple sources that say Gridview's do not persist the Gridview.DataSource property on postback. My understanding is that in term's of ASP.NET, a postback is any page load that is not the first pageload (see MSDN).
I've got a situation with 2 very similar gridviews.
GvOne.DataSource is null on postback.
GvTwo.DataSource is NOT null on postback.
The only big difference outside of a few differing columns is GvOne is populated with the Entity Framework and LINQ. GvTwo is populated by a DataTable filled by a SqlDataAdapter.
Further, GvOne and GvTwo have a TemplateField with a TextBox that I use to gather user input. Both use the same code to pull the TextBox.Text on postback:
TextBox tb = (TextBox)GvOne.Rows[i].FindControl("actualTxt");
GvOne properly gathers tb.Text. GvTwo always finds the tb.Text value to be 0.
Basic Gridview code:
<asp:GridView ID="GvOne" runat="server" AutoGenerateColumns="False">
<Columns>
<asp:TemplateField HeaderText="Return">
<ItemTemplate>
<asp:TextBox id="actualTxt" runat="server" Text='0' Width="40px"></asp:TextBox>
</ItemTemplate>
</asp:TemplateField>
...
</Columns>
</asp:GridView>
<asp:GridView ID="GvTwo" runat="server" AutoGenerateColumns="False">
<Columns>
<asp:TemplateField HeaderText="Order">
<ItemTemplate>
<asp:TextBox id="actualTxt" runat="server" Text='0' Width="40px"></asp:TextBox>
</ItemTemplate>
</asp:TemplateField>
...
</Columns>
</asp:GridView>
Changing GvTwo to use Entity Framework and LINQ is a potential solution, albeit a major undertaking. Does anyone know what is going on here?
UPDATE (See my comment on Joel Etherton's Answer)
Due to popular demand here is the code to populate the gridview within Page_Load event for GvTwo (GvOne is similar):
ordersGV.DataSource = dataSetObject.Tables["activeParts"];
ordersGV.DataBind();
Searching through the code behind I found no other references to ordersGv.Datasource and no other events that are hooked into associated with the page life cycle.
Gridviews do not persist the datasource across postbacks. If you have a gridview that has a non-null datasource then you must be filling that datasource somewhere in your code. It would be instructive to travel through your event cycle to find where exactly the population of the datasource is occuring on postback.
what does your Page_load code look like?
GridView does not keep DataSource property populated over the postbacks for performance issues
Maybe the second gridview is rebinding the datasource on postback?
I have a LinqDataSource and a GridView displaying a table. Columns of type xml don't show up. I'd like them to show up something like they do in Sql Server query outputs, with a link to display the clickable xml, though there may be another approach i'm not considering (maybe a styled display of the xml data, etc.). Two things i'd like to know how to do
First, get the xml converted to a string, and display it in the table. Maybe the first 30 chars.
Finally, style the xml into something useful, like a clickable link to display the full xml, or a sub-table, or a styled string.
So the following works, and displays a nice table, with edit and delete links. But Xml fields are missing. How would you go about adding support for the Xml fields?
<form id="form1" runat="server">
<div>
<asp:LinqDataSource ID="OrdersDataSource"
OnContextCreating="LinqDataSource_ContextCreating"
runat="server" ContextTypeName="MyDbDataContext"
EnableUpdate="True" TableName="orders"
EnableDelete="true"
OrderBy="Status, UserId">
</asp:LinqDataSource>
<asp:GridView ID="OrdersGridView" DataSourceID="OrdersDataSource"
CssClass="gridview" PageSize="30" AutoGenerateDeleteButton="true"
AutoGenerateEditButton="true" AllowPaging="True" AllowSorting="True"
AlternatingRowStyle-BackColor="Beige" DataKeyNames="OrderId"
runat="server" Width="705px">
<Columns>
</Columns>
</asp:GridView>
</div>
</form>
Page_Load is empty at the moment.
The best approach would be to use GridView's RowDataBound event. This would look something like this:
protected void OrdersGridView_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
var dataItem = e.Row.DataItem;
...
}
}
I'm not sure what the type of dataItem is, but probably you can cast it to your order type (easiest to see in the debugger, just set a breakpoint in the RowDataBound event handler). You should be able to get your xml data from this object. When you have the xml data, you can convert it to a string and get the first 30 characters, for example.
The last thing to do is set this text in the correct cell. See the example on MSDN for this.