GridView event raise unexpectedly after postback - asp.net

Consider this Asp.net page code:
<head runat="server">
<title></title>
<script type="text/javascript">
function showhide(master, detail) {
var src = $(master).children()[0].src;
if (src.endsWith("plus.png"))
src = src.replace('plus.png', 'minus.png');
else
src = src.replace('minus.png', 'plus.png');
$(master).children()[0].src = src;
$(detail).slideToggle("normal");
}
</script>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server">
<Scripts>
<asp:ScriptReference Path="~/scripts/jquery-1.6.2.min.js" ScriptMode="Release" />
</Scripts>
</asp:ScriptManager>
<div>
<asp:SqlDataSource ID="sqlDsCustomers" runat="server" ConnectionString="<%$ ConnectionStrings:Northwind %>"
SelectCommand="SELECT [Customers].[CustomerID], [Customers].[CompanyName], COUNT([OrderID]) TotalOrders
FROM [Customers] INNER JOIN [Orders] ON [Customers].[CustomerID]=[Orders].[CustomerID]
Group By [Customers].[CustomerID], [Customers].[CompanyName]">
</asp:SqlDataSource>
<asp:GridView Width="100%" AllowPaging="True" ID="gvCustomers" AutoGenerateColumns="False"
DataSourceID="sqlDsCustomers" runat="server" ShowHeader="False" OnRowCreated="gvCustomers_RowCreated">
<Columns>
<asp:TemplateField>
<ItemTemplate>
<div class="group" id='<%#String.Format("customer{0}",Container.DataItemIndex) %>'
onclick='showhide(<%#String.Format("\"#customer{0}\"",Container.DataItemIndex) %>,<%#String.Format("\"#order{0}\"",Container.DataItemIndex) %>)'>
<asp:Image ID="imgCollapsible" CssClass="first" ImageUrl="~/Assets/img/plus.png"
Style="margin-right: 5px;" runat="server" /><span class="header">
<%#Eval("CustomerID")%>
:
<%#Eval("CompanyName")%>
(<%#Eval("TotalOrders")%>Orders) </span>
</div>
<div id='<%#String.Format("order{0}",Container.DataItemIndex) %>' class="order">
<asp:GridView AutoGenerateColumns="false" CssClass="grid" ID="ddd" runat="server"
ShowHeader="true" EnableViewState="false">
<RowStyle CssClass="row" />
<AlternatingRowStyle CssClass="altrow" />
<Columns>
<asp:TemplateField ItemStyle-CssClass="rownum">
<ItemTemplate>
<%# Container.DataItemIndex + 1 %>
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField HeaderText="Order ID" DataField="OrderID" ItemStyle-Width="80px" />
<asp:BoundField HeaderText="Date Ordered" DataField="OrderDate" DataFormatString="{0:MM/dd/yyyy}"
ItemStyle-Width="100px" />
<asp:BoundField HeaderText="Date Required" DataField="RequiredDate" DataFormatString="{0:MM/dd/yyyy}"
ItemStyle-Width="110px" />
<asp:BoundField HeaderText="Freight" DataField="Freight" DataFormatString="{0:c}"
ItemStyle-Width="50px" ItemStyle-HorizontalAlign="Right" />
<asp:BoundField HeaderText="Date Shipped" DataField="ShippedDate" DataFormatString="{0:MM/dd/yyyy}"
ItemStyle-Width="100px" />
</Columns>
</asp:GridView>
</div>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
</div>
<asp:Button ID="Button1" runat="server" onclick="Button1_Click" Text="Button" />
</form>
and the code behind:
protected void Page_Load(object sender, EventArgs e)
{
}
protected void gvCustomers_RowCreated(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
string custID = ((DataRowView)e.Row.DataItem)["CustomerID"].ToString();
using (DataClassesDataContext dc=new DataClassesDataContext())
{
List<Order> ord = (from o in dc.Orders
where o.CustomerID == custID.Trim()
select o).ToList();
GridView ctrl = e.Row.FindControl("ddd") as GridView;
ctrl.DataSource = ord;
}
}
}
protected void Button1_Click(object sender, EventArgs e)
{
Response.Redirect("Default.aspx");
}
the problem is when I Click on Button1 to redirect to another page gvCustomers_RowCreated raise and I get Null reference error.Why this event raised after postback?
EDIT 1):
I removed SqlDataSource and bind GridView in the code behind like this:
public partial class Default2 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
using (DataClassesDataContext dc=new DataClassesDataContext())
{
var query = dc.ExecuteQuery<clsRetern>("SELECT [Customers].[CustomerID], [Customers].[CompanyName], COUNT([OrderID]) TotalOrders FROM [Customers] INNER JOIN [Orders] ON [Customers].[CustomerID]=[Orders].[CustomerID] Group By [Customers].[CustomerID], [Customers].[CompanyName]").ToList();
List<clsRetern> ret = new List<clsRetern>();
foreach (var item in query)
{
clsRetern r = new clsRetern();
r.CompanyName = item.CompanyName;
r.CustomerID = item.CustomerID;
r.TotalOrders = item.TotalOrders;
ret.Add(r);
}
gvCustomers.DataSource = ret;
gvCustomers.DataBind();
}
}
}
protected void gvCustomers_RowCreated(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
string custID = ((clsRetern)e.Row.DataItem).CustomerID.Trim();
using (DataClassesDataContext dc=new DataClassesDataContext())
{
List<Order> ord = (from o in dc.Orders
where o.CustomerID == custID.Trim()
select o).ToList();
GridView ctrl = e.Row.FindControl("ddd") as GridView;
ctrl.DataSource = ord;
}
}
}
protected void Button1_Click(object sender, EventArgs e)
{
Response.Redirect("Default.aspx",true);
}
}
public class clsRetern
{
public string CustomerID { get; set; }
public string CompanyName { get; set; }
public int TotalOrders { get; set; }
}
I tried Response.Redirect("Default.aspx"); but the problem still remains.

If you understand ASP.NET page model then this question should not come to you. Every time request goes to ASP.NET page, a new page object is created along with entire control tree and the state is managed using view-state in post-back scenarios. So in your case, whenever post-back happens, a new page and grid-view is created. The data for the gid-view would be persisted in thew view-state and grid would be bound to that data.
The RowCreated event is raised whenever grid row is created regardless of whether DataBind is explicitly called or not. The intent of the event is so that one can tweak with UI (grid-view cells) - e.g. some controls can be pushed into grid-view cells if required. The same should happen regardless of post-back scenario other wise those dynamic controls will not get created. Typically, these controls will restore their state using view-state and your get your grid-view UI (control tree) back as it was in first page cycle.

Thats because even if you fire Response.Redirect, the Page_Load event would still be raised and if your GridView binding code is not inside !IsPostBack, that code will be hit.
Try this.
protected void Page_Load(object sender, EventArgs e)
{
try
{
if (!IsPostBack)
{
//bind gridview here
}
}
catch (Exception exception)
{
//Elmah.ErrorSignal.FromCurrentContext().Raise(exception);
}
}
Savvy?

Add EnableViewState=False to the page directive to prevent "RowCreated" running on every post back.

Related

Trying to increment an integer on click of checkboxes

I'm having a scope problem (I think) trying to increment an integer.
I have several CheckBoxes, and want to limit users to only choose a maximum of three. So, I want to increment an integer on each checkbox click which I can check against.
My Default.aspx
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False">
<Columns>
<asp:TemplateField>
<ItemTemplate>
<asp:CheckBox ID="chkRow" runat="server" autopostback="true" OnCheckedChanged="MyCheckBoxes_SelectedIndexChanged"/>
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField DataField="Title" />
<asp:BoundField DataField="Publisher" />
<asp:BoundField DataField="Genre" />
</Columns>
</asp:GridView>
My Default.aspx.cs
public partial class _Default : Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!this.IsPostBack)
{
DataSet ds = new DataSet();
ds.ReadXml(MapPath("App_Data/publications.xml"));
GridView1.DataSource = ds;
GridView1.DataBind();
}
}
private static int mycount = 0;
protected void MyCheckBoxes_SelectedIndexChanged(object sender, EventArgs e)
{
Label1.Text = mycount.ToString();
Increment(mycount);
}
public static void Increment(int mycount)
{
mycount++;
}
}
My Label1 is always displaying 0.
The names overlap inside the method scope of Increment. Remove mycount as parameter in Increment method

asp button on click inside item template not firing

Not able to make this work.
<asp:TemplateField>
<ItemTemplate>
<asp:Button ID="btnApprove" runat="server" Text="Approve" OnClick ="btnApprove_Click" />
</ItemTemplate>
</asp:TemplateField>
code behind:
protected void btnApprove_Click(object sender, EventArgs e)
{
Response.Redirect("viewprofile.aspx");
}
not even firing when button is clicked. any tricks on this?
Set EnableEventValidation="false" right at the top in your Page directive:
<%# Page EnableEventValidation="false" Language="C#"...
Just beware that setting this value to false can expose your website to security vulnerabilities.As an alternative, instead of setting EnableEventValidation="false" you can handle the grid views OnRowCommand:
.ASPX:
<asp:GridView ID="GridView1" runat="server" OnRowCommand="GridView1_RowCommand">
<Columns>
<asp:TemplateField>
<ItemTemplate>
<asp:Button runat="server" Text="Approve" CommandName="Approve" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
Code behind:
public partial class delete_me : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)//THIS IS IMPORTANT.GridView1_RowCommand will not fire unless you add this line
{
var p1 = new Person() { Name = "Person 1" };
var p2 = new Person() { Name = "Person 2" };
var list = new List<Person> { p1, p2 };
GridView1.DataSource = list;
GridView1.DataBind();
}
}
protected void GridView1_RowCommand(object sender, GridViewCommandEventArgs e)
{
System.Diagnostics.Debugger.Break();
}
}
public class Person
{
public string Name { get; set; }
}
You Just put on your gridview.
<asp:TemplateField>
<ItemTemplate>
<asp:Button ID="Button1" runat="server" Text="Button" OnClick="Button1_Click"/>
</ItemTemplate>
</asp:TemplateField>
Also put code behind
protected void Button1_Click(object sender, EventArgs e)
{
Response.Redirect("WebForm1.aspx");
}
Try!!!! it's working fine...

One Checkbox to control gridview rows

How can I implement the following: I would like to have a checkbox on the top of my gridview. When checkbox is checked should show all the hidden rows from the gridview but when is unchecked should only show the unhide ones.
I only need 1 checkbox at the top(not in the header of the gridview), I found couple of examples but all of the have checkbox as column and then added to the header as well.
What would be the best way to do this?
Thanks in advance.
This is a one way of doing it on the server:
I define the Gridview and Checkbox as follows:
<asp:CheckBox id="cbShowHidden" runat="server" Text="Show Hidden Rows"
Checked="true" oncheckedchanged="cbShowHidden_CheckedChanged" AutoPostBack="true" ></asp:CheckBox>
<br />
<asp:GridView ID="gvTest" AutoGenerateColumns="false" runat="server"
ShowHeader="true" onrowdatabound="gvTest_RowDataBound">
<Columns>
<asp:BoundField DataField="id" HeaderText="id" />
<asp:BoundField DataField="name" HeaderText="name" />
<asp:BoundField DataField="family" HeaderText="family" />
<asp:BoundField DataField="visibility" HeaderText="visibility" />
</Columns>
</asp:GridView>
This is the codebehind:
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
PopulateGrid();
}
}
private void PopulateGrid()
{
gvTest.DataSource = Enumerable.Range(0, 3000).Select(i => new { id = i, name = 2 * i, family = "Unknown", visibility = i % 3 == 0 ? "Visible" : "Hidden" });
gvTest.DataBind();
}
protected void gvTest_RowDataBound(object sender, GridViewRowEventArgs e)
{
bool showHidden = cbShowHidden.Checked;
if (e.Row.RowType == DataControlRowType.DataRow)
e.Row.Visible = showHidden || string.Compare(e.Row.Cells[3].Text, "Visible") == 0;
}
protected void cbShowHidden_CheckedChanged(object sender, EventArgs e)
{
PopulateGrid();
}
This can be greatly optimized by doing the hiding on the client with jQuery.

How to bind a Control to <ItemTemplate> in GridView?

Edit: My object is defined as below. I passed it into GridView1.
public class UserTestorViewModel
{
public string Username {get;set;}
public string Password {get;set;}
public Label SuiteName {get;set;}
}
I have GridView bound to a customized List<UserTestorViewModel>:
this.GridView1.DataSource = utViewModelList;
this.GridView1.DataBind();
and in the .aspx I have
<asp:TemplateField HeaderText="LoginName">
<ItemTemplate>
<asp:Label ID="LoginName" runat= "server" Text= '<%# ((ViewModels.UserTestorViewModel)Container.DataItem).User.userName %> '></asp:Label>
</ItemTemplate>
</asp:TemplateField>
this works because ViewModels.UserTestorViewModel.User.userName is string, but
<asp:TemplateField HeaderText="SuiteName">
<ItemTemplate>
<%# ((ViewModels.UserTestorViewModel)Container.DataItem).SuiteName %>
</ItemTemplate>
</asp:TemplateField>
because ViewModels.UserTestorViewModel.SuiteName is Label from System.Web.UI.WebControls
So how to bind a System.Web.UI.WebControls to <ItemTemplate> NOT the Text of the System.Web.UI.WebControls
You may place a Placeholder control in ItemTemplate and put your label control to it on the RowCreated event of the GridView:
protected void Page_Init(object sender, EventArgs e)
{
GridView1.RowCreated += new GridViewRowEventHandler(GridView1_RowCreated);
}
void GridView1_RowCreated(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
var dataItem = (KeyValuePair<int, Label>)e.Row.DataItem;
var nameLabelPlaceholder = e.Row.FindControl("NameLabelPlaceholder") as PlaceHolder;
if (nameLabelPlaceholder != null)
{
nameLabelPlaceholder.Controls.Add(dataItem.Value);
}
}
}
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
GridView1.DataSource = GetGridViewDataSource();
GridView1.DataBind();
}
}
private object GetGridViewDataSource()
{
return (from item in Enumerable.Range(1, 10)
select new KeyValuePair<int, Label>(item, new Label() { ID = string.Format("NameLabel_{0}", item), Text = string.Format("Item #{0}", item), ForeColor = System.Drawing.Color.Red }))
.ToDictionary(kvp1 => kvp1.Key, kvp2 => kvp2.Value);
}
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="false" DataKeyNames="Key">
<Columns>
<asp:BoundField HeaderText="Id" DataField="Key" />
<asp:TemplateField HeaderText="Name">
<ItemTemplate>
<asp:PlaceHolder runat="server" ID="NameLabelPlaceholder" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>

ASP.NET - Problem with GridView with Dynamic Columns

I have a GridView that includes BoundField and TemplateField elements. Some of the TemplateField elements are dynamically generated. For the sake of reference, here is the GridView that I am using
<asp:GridView ID="myGridView" runat="server" AutoGenerateColumns="False"
DataKeyNames="ID" ShowFooter="True" EnableModelValidation="True"
OnLoad="myGridView_Load" OnRowCommand="myGridView_RowCommand"
OnRowEditing="myGridView_RowEditing" OnRowDeleting="myGridView_RowDeleting"
OnRowCancelingEdit="myGridView_RowCancelingEdit"
OnRowUpdating="myGridView_RowUpdating">
<Columns>
<asp:BoundField DataField="Number" Visible="true" HeaderText="Test" />
<asp:TemplateField HeaderText="Number">
<EditItemTemplate>
<asp:TextBox ID="tb1" runat="server" Text='<%# Bind("Number") %>' />
</EditItemTemplate>
<ItemTemplate>
<asp:Label ID="lb1" runat="server" Text='<%# Bind("Number") %>' />
</ItemTemplate>
<FooterTemplate>
<asp:TextBox ID="ftb" runat="server" Text="[x]" />
</FooterTemplate>
</asp:TemplateField>
<%-- Dynamically Generated Columns Will Be Inserted Here --%>
<asp:TemplateField HeaderText="Actions">
<EditItemTemplate>
<asp:LinkButton ID="ulb" runat="server" Text="update" CommandName="Update" />
<asp:LinkButton ID="clb" runat="server" Text="cancel" CommandName="Cancel" />
</EditItemTemplate>
<FooterTemplate>
<asp:LinkButton ID="slb" runat="server" Text="insert"
OnClick="saveLinkButton_Click" />
</FooterTemplate>
<ItemTemplate>
<asp:LinkButton ID="elb" runat="server" Text="edit" CommandName="Edit" />
<asp:LinkButton ID="dlb" runat="server" Text="delete" CommandName="Delete" />
</ItemTemplate>
</asp:TemplateField
</Columns>
<EmptyDataTemplate>
<table border="0" cellpadding="0" cellspacing="0">
<tr><td>Number</td></tr>
<tr>
<td><asp:TextBox ID="ntb" runat="server" /></td>
<td><asp:LinkButton ID="slb2" runat="server" Text="save" OnClick="saveLinkButton_Click" /></td>
</tr>
</table>
</EmptyDataTemplate>
</asp:GridView>
When the GridView initially loads, everything is loaded properly. However, anytime I perform a command (edit, insert, delete), all of the data goes away. Oddly, the BoundField values still appear correctly. However, any TemplateField does not seem to get rendered. The code that I am using to work with this GridView is here:
public partial class GridView : System.Web.UI.Page
{
private List<string> dynamicColumnNames = new List<string>();
protected void Page_Load(object sender, EventArgs e)
{
LoadPageData();
}
protected void saveLinkButton_Click(object sender, EventArgs e)
{
}
protected void myGridView_Load(object sender, EventArgs e)
{
if (Page.IsPostBack == false)
{
BindGridData();
}
}
protected void myGridView_RowCommand(object sender, GridViewCommandEventArgs e)
{ }
protected void myGridView_RowEditing(object sender, GridViewEditEventArgs e)
{
myGridView.EditIndex = e.NewEditIndex;
BindGridData();
}
protected void myGridView_RowDeleting(object sender, GridViewDeleteEventArgs e)
{
LoadPageData();
BindGridData();
}
protected void myGridView_RowCancelingEdit(object sender, EventArgs e)
{
myGridView.EditIndex = -1;
BindGridData();
}
protected void myGridView_RowUpdating(object sender, GridViewUpdateEventArgs e)
{
myGridView.EditIndex = -1;
LoadPageData();
BindGridData();
}
private void BindGridData()
{
// Create a temporary data source
DataTable tempData = new DataTable();
tempData.Columns.Add("ID");
tempData.Columns.Add("Number");
// Dynamically add template columns
foreach (string columnName in dynamicColumnNames)
{
tempData.Columns.Add(columnName);
TemplateField templateField = new TemplateField();
templateField.HeaderTemplate = new MyTemplateField(ListItemType.Header, columnName);
templateField.ItemTemplate = new MyTemplateField(ListItemType.Item, columnName);
templateField.EditItemTemplate = new MyTemplateField(ListItemType.EditItem, columnName);
templateField.FooterTemplate = new MyTemplateField(ListItemType.Footer, columnName);
myGridView.Columns.Insert(2, templateField);
}
// Add some phony data
Random random = new Random(DateTime.Now.Millisecond);
for (int i = 0; i < 10; i++)
{
DataRow tempRow = tempData.NewRow();
tempRow["Number"] = (i + 1);
foreach (string column in dynamicColumnNames)
tempRow[column] = random.NextDouble();
tempData.Rows.Add(tempRow);
}
// Bind the data to the grid
myGridView.DataSource = tempData;
myGridView.DataBind();
}
private void LoadPageData()
{
dynamicColumnNames.Add("USA");
dynamicColumnNames.Add("Japan");
dynamicColumnNames.Add("Mexico");
}
}
internal class MyTemplateField : ITemplate
{
// The type of the list item
private ListItemType listItemType;
// The value to use during instantiation
private string value1 = string.Empty;
public MyTemplateField(ListItemType listItemType, string value1)
{
this.listItemType = listItemType;
this.value1 = value1;
}
public void InstantiateIn(Control container)
{
if (listItemType == ListItemType.Item)
{
TextBox textBox = new TextBox();
textBox.ReadOnly = true;
textBox.DataBinding += new EventHandler(textBox_DataBinding);
container.Controls.Add(textBox);
}
else if (listItemType == ListItemType.EditItem)
{
TextBox textBox = new TextBox();
textBox.DataBinding += new EventHandler(textBox_DataBinding);
container.Controls.Add(textBox);
}
else if (listItemType == ListItemType.Header)
{
Literal literal = new Literal();
literal.Text = value1;
container.Controls.Add(literal);
}
else if (listItemType == ListItemType.Footer)
{
TextBox textBox = new TextBox();
container.Controls.Add(textBox);
}
}
private void textBox_DataBinding(object sender, EventArgs e)
{
TextBox textBox = (TextBox)(sender);
GridViewRow row = (GridViewRow)(textBox.NamingContainer);
textBox.Text = DataBinder.Eval(row.DataItem, value1).ToString();
}
}
How do I use commands in a GridView with Dynamically added columns? What is wrong with my code?
Thank you!
Dynamically added controls must be added on each post, you can't just add these controls on the first page load. Try removing your BindGridData() from within the Page.IsPostBack == false.

Resources