I've created a very simple bit of code in C# to display data in an asp:Table. On the initial page load all looks great. I've used an AJAX timer to refresh every 15 seconds to keep the data up to date. The problem is after it refreshes the table shows all the data duplicated. I've tried debugging and again all looks good until the AJAX refresh and then it seems to jump all over the place in the C# code rather than following the expected sequence.
Hopefully I've not approached this in completely the wrong way but if I have please tell me! Is there a way to prevent/fix this duplication?
I've replicated the problem code here in a very simple form. Default.aspx:
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="default.aspx.cs" Inherits="testASPTables._default" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>
<asp:Table ID="Table1" runat="server">
</asp:Table>
<asp:Timer ID="Timer1" runat="server" Interval="15000" OnTick="Timer1_Tick">
</asp:Timer>
</div>
</form>
</body>
</html>
...and the codebehind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace testASPTables
{
public partial class _default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
TableRow[] tRows = new TableRow[5];
TableCell[] tCells = new TableCell[5];
for (int i = 0; i < 5; i++)
{
tRows[i] = new TableRow();
Table1.Rows.Add(tRows[i]);
tCells[i] = new TableCell();
tRows[i].Cells.Add(tCells[i]);
tCells[i].Text = "Cell Number: " + i.ToString();
}
}
protected void Timer1_Tick(object sender, EventArgs e)
{
Page_Load(sender, e);
}
}
}
You're calling Page_Load twice. Page_Load runs on postback, then you call it again from Timer1_Click.
You need to always be concious of whether the page is in a postback or not and how Page_Load works.
Here's what I'd do.
protected void Page_Load(object sender, EventArgs e)
{
if(!IsPostBack)
{
LoadTableData();
}
}
protected void LoadTableData()
{
Table1.Rows.Clear(); //I added this
TableRow[] tRows = new TableRow[5];
TableCell[] tCells = new TableCell[5];
for (int i = 0; i < 5; i++)
{
tRows[i] = new TableRow();
Table1.Rows.Add(tRows[i]);
tCells[i] = new TableCell();
tRows[i].Cells.Add(tCells[i]);
tCells[i].Text = "Cell Number: " + i.ToString();
}
}
protected void Timer1_Tick(object sender, EventArgs e)
{
LoadTableData();
}
Oh, and in addition to that, you'd need to clear the existing table rows.
By the way, this is very inefficient. You're reloading the entire table every 15 seconds. Multiple that by a few clients and you're practically performing a DoS attack on yourself. Consider using SignalR to push new changes to the clients as rows are added to the underlying data.
Related
In this program I am dynamically adding CheckBox (by taking input from textbox) in CheckBoxList which itself is created dynamically.
Now when I add checkbox and remove it, everything works fine. When I again try to add checkbox, it is adding in the ArrayList as well as CheckboxList but not showing on the page.
Working on this from past 2 days but couldn't figure out. Please Help!!!
Thank You!
<!--chkboxlist.aspx-->
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Sample</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:TextBox ID="txtchkboxname" runat="server" AutoPostBack="true"></asp:TextBox>
</div>
</form>
</body>
</html>
//chkboxlist.aspx.cs
using System;
using System.Collections;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
public partial class chkboxlist : System.Web.UI.Page
{
static ArrayList arlst = new ArrayList();
CheckBoxList chklst = new CheckBoxList();
protected void Page_Load(object sender, EventArgs e)
{
if (txtchkboxname.Text != "")
{
arlst.Add(txtchkboxname.Text);
txtchkboxname.Text = "";
}
chklst.DataSource = arlst;
chklst.DataBind();
chklst.AutoPostBack = true;
chklst.SelectedIndexChanged += new EventHandler(this.chklst_OnSelectedIndexChanged);
form1.Controls.Add(chklst);
}
protected void chklst_OnSelectedIndexChanged(object sender, EventArgs e)
{
arlst.RemoveAt(chklst.SelectedIndex);
chklst.DataSource = arlst;
chklst.DataBind();
}
}
I am very new to ASP.NET but I have background in C and C#. I am trying to make web application which will be connected with my database. I did that and that is OK. The problem occurred when I added buttons for moving forward and backward through the database... It was working fine but only for one click! Then I thought that probably I have bug in the code and I decided to do it in easy way.
I created new web form with just one button and one label, but behavior is still the same- after first click event is not fired again. Any help?
My code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace DummyTest
{
public partial class WebForm1 : System.Web.UI.Page
{
int pom = 0;
protected void Page_Load(object sender, EventArgs e)
{
Label1.Text = Convert.ToString(pom);
}
protected void Button1_Click(object sender, EventArgs e)
{
pom++;
Label1.Text = Convert.ToString(pom);
}
}
}
and source
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm1.aspx.cs"
Inherits="DummyTest.WebForm1" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
Dummy Test
<br />
<asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="Button" />
<asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>
<br />
</div>
</form>
</body>
Your Webform class gets recreated on every request - that's just how it works. You need to persist the field pom in some other way. You could add it to the session state, but that affects scalability of the application as you then hold state on the server.
It is already persisted by the label control, so you could do something like this in your click event:
var pom = Int.Parse(Label1.Text);
pom++;
Label1.Text = pom.ToString();
However, this will not always be the case for something you want to persist. In these cases I would add a hidden field to the html which holds it. My WebForms is a little rusty, but in your markup:
<asp:HiddenField id="pom" value="0" />
then pull the value out and increment in the click event as I've done above.
Whenever you click your int pom = 0 get initialized to zero and then you set value that is why it looks it calling once. either make it static or better to try:
protected void Button1_Click(object sender, EventArgs e)
{
Label1.Text = "" + (Convert.ToInt32(Label1.Text) + 1);
}
You could try moving your initialisation into an IsPostBack check, so reloading the page does not re-intitialise the variable to 0. The IsPostBack value indicates whether the page is being rendered for the first time or is being loaded in response to a postback and a button click event triggers a postback.
Additional
The IsPostBack fires at a specific stage in the page lifecycle
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
int pom = 0;
}
Label1.Text = Convert.ToString(pom);
}
To some extent it depends what the purpose of the pom variable is. If it is tracking clicks per user then the above should help. However if you're tracking clicks across all users then you should consider something else (probably using the global.asax)
It is working now! Here is code for Up and Down.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace DummyTest
{
public partial class WebForm1 : System.Web.UI.Page
{
static int pom;
protected void Page_Load(object sender, EventArgs e)
{
if (Page.IsPostBack == false)
{
pom = 0;
Label1.Text = Convert.ToString(pom);
}
}
protected void Button1_Click(object sender, EventArgs e)
{
var pom = Int32.Parse(Label1.Text);
pom++;
Label1.Text = Convert.ToString(pom);
}
protected void Button2_Click(object sender, EventArgs e)
{
var pom = Int32.Parse(Label1.Text);
pom--;
Label1.Text = Convert.ToString(pom);
}
}
}
I'm using VisualStudio 2013 Express for WEB, and I created an empty web application, and then added a new web form.
I'm trying to learn ASP.NET, and I learned that in the session / page life cycle there is Page_Load where I can execute thing that will happen just after the page loads. The thing is I learned that it is possible to put the Page_Load event under <script> before the <HTML> tag.
(The aspx file name is Register) When I try this:
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="Register.aspx.cs" Inherits="Learning.Register" %>
<!DOCTYPE html>
<script runat="server">
int randomnumber = 0;
public void Page_Load()
{
Random rnd = new Random();
randomnumber = rnd.Next(100000, 1000000);
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<% Response.Write(randomnumber); %>
</div>
</form>
</body>
</html>
All I get is 0 for some reason.
Also, I noticed that under Register.aspx.cs I have this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace Learning
{
public partial class Register : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
}
}
You can definitely put the server side code in aspx using script tag. But the page load syntax should exactly match as it is in your code behind(.cs file)
Page_Load(object sender, EventArgs e)
In your script code you have just Page_Load() which does not trigger at all.
If you need this to be excecuted then you need to remove the function in the code behind and replace the method signature in the script tag
in the script tag you need to have base.OnLoad(e)
protected void Page_Load(object sender, EventArgs e)
{
base.OnLoad(e)
Random rnd = new Random();
randomnumber = rnd.Next(100000, 1000000);
}
That's because you are overriding the behavior of the base class, and if you don't call base.OnLoad(e) then Page_Load would not run in the markup side.
I'm doing some test with dynamic controls. Here the code:
ASPX page
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:DropDownList ID="DropDownList1" runat="server" AutoPostBack="True"
onselectedindexchanged="DropDownList1_SelectedIndexChanged">
<asp:ListItem Value="0">Nothing</asp:ListItem>
<asp:ListItem Value="1">Two buttons</asp:ListItem>
</asp:DropDownList>
<asp:Panel ID="Panel1" runat="server">
</asp:Panel>
</div>
</form>
</body>
</html>
Code behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (ViewState["CreateDynamicButton"] != null)
{
CreateControls();
}
}
protected void DropDownList1_SelectedIndexChanged(object sender, EventArgs e)
{
CreateControls();
}
void Button_Click(object sender, EventArgs e)
{
Button b = (Button)sender;
Response.Write("You clicked the button with ID " + b.ID);
}
private void CreateControls()
{
if (DropDownList1.SelectedValue.Equals("1"))
{
Panel1.Controls.Clear();
Button b1 = new Button();
b1.ID = "b1";
b1.Text = "Button 1";
Button b2 = new Button();
b2.ID = "b2";
b2.Text = "Button 2";
b1.Click += new EventHandler(Button_Click);
b2.Click += new EventHandler(Button_Click);
Panel1.Controls.Add(b1);
Panel1.Controls.Add(b2);
ViewState["CreateDynamicButton"] = true;
}
}
}
This code works but as you can see I remove all controls in the Panel1.Controls before add the buttons becouse when I choose to create them for the second time I get an exeption for duplicated controls ID.
I think that for two buttons the operation is very fast but with a larger number of controls the elaboration time will be longer.
Can you suggest me a better way to re-generate controls after PostBack without this workaround?
For starters, if you're trying to remove the controls (in this case buttons) for good, are you best off using the Dispose method? clearing the panel of the control won't dispose of it, which would explain the already in use ID's. You can do a simple loop to do this, regardless of how many controls are in the panel;
foreach(Control control in Container)
{
control.Dispose();
}
also, to create buttons i see you're just naming them btn1, btn2 and so on. You could do this in a loop too;
for(int i = 0; i >= yourInt; i++)
{
Button b = new Button();
b.ID = "b" + i;
b.Text = "Button " + i;
}
}
Create a class variable private bool ControlsCreated = false;
In your CreateControls method then check
if (!ControlsCreated) {
//your code to create controls
}
This makes sure the controls are created only once. If at any point later you need to recreate the controls (dropdownlist value changed), just clear the container and set ControlsCreated to false.
Basically, in a nutshell, the problem is that dynamically generated triggers for an UpdatePanel can no longer be found (by ASP.NET) as soon as I add them as children of a custom control.
Since the amount of code I'm working on is quite substantial I've recreated the problem on a smaller scale, which will make it easier to debug.
The error thrown in my face is:
A control with ID 'theTrigger' could not be found for the trigger in UpdatePanel 'updatePanel'.
I'm not sure whether this implementation of a "custom control" is the right way to go about it, but I did not write the original implementation: I'm working with code written by a previous developer to which I cannot make large modifications. It looks a little unusual to me, but, alas, this is what I've been given.
Default.aspx
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="TestWeb.Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Panel runat="server" ID="panel">
</asp:Panel>
<asp:ScriptManager ID="scriptManager" runat="server"></asp:ScriptManager>
<asp:UpdatePanel runat="server" ID="updatePanel" UpdateMode="Conditional">
<ContentTemplate>
<asp:Label ID="lblSomething" runat="server"></asp:Label>
</ContentTemplate>
</asp:UpdatePanel>
</div>
</form>
</body>
</html>
Default.aspx.cs
using System;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace TestWeb
{
public partial class Default : Page
{
protected void Page_Load(object sender, EventArgs e)
{
UselessTableWrapper table = new UselessTableWrapper();
TableRow tr = new TableRow();
TableCell td = new TableCell();
LinkButton button1 = new LinkButton { ID = "theTrigger", Text = "Click Me" };
button1.Click += button1_Click;
td.Controls.Add(button1);
tr.Controls.Add(td);
table.AddRow(tr);
panel.Controls.Add(table);
// ### uncomment these lines (and comment the one above) to see it working
// ### without the custom control
/*
Table realTable = new Table();
realTable.Controls.Add(tr);
panel.Controls.Add(realTable);
*/
updatePanel.Triggers.Add(new AsyncPostBackTrigger { ControlID = "theTrigger", EventName = "Click" });
scriptManager.RegisterAsyncPostBackControl(button1);
}
protected void button1_Click(object sender, EventArgs e)
{
lblSomething.Text = "Random number: " + new Random().Next(100);
updatePanel.Update();
}
}
}
MyControl.cs
using System;
using System.Web.UI.WebControls;
namespace TestWeb
{
public class UselessTableWrapper : WebControl
{
private Table table = new Table();
protected override void OnPreRender(EventArgs e)
{
Controls.Add(table);
}
public void AddRow(TableRow row)
{
table.Controls.Add(row);
}
}
}
Any ideas would be greatly appreciated.
Edit
I've tried switching the OnPreRender event for this (found in a tutorial):
protected override void RenderContents(HtmlTextWriter writer)
{
writer.BeginRender();
table.RenderControl(writer);
writer.EndRender();
base.RenderContents(writer);
}
... hoping that it would fix it, but it does not.
this is the approach that I've taken with loading a ascx web control inside an aspx control from the code behind.
In the control:
namespace dk_admin_site.Calculations
{
public partial class AssignedFieldCalculation : System.Web.UI.UserControl
{
public static AssignedFieldCalculation LoadControl(Calculation initialData)
{
var myControl = (AssignedFieldCalculation) ((Page) HttpContext.Current.Handler).LoadControl(#"~\\Calculations\AssignedFieldCalculation.ascx");
myControl._initialData = initialData;
return myControl;
}
private Calculation _initialData;
public Calculation Data { get { return _initialData; } }
protected void Page_Load(object sender, EventArgs e) {}
}
}
in the web form code behind:
protected void Page_Load(object sender, EventArgs e)
{
if (this.IsPostBack)
{
if (ScriptManager1.AsyncPostBackSourceElementID.StartsWith("ctl00$MainContent$calc") && ScriptManager1.AsyncPostBackSourceElementID.EndsWith("$btnRemoveCalculationFromField"))
{
//do something on the postback
}
else if (ScriptManager1.AsyncPostBackSourceElementID.StartsWith("ctl00$MainContent$calc") && (ScriptManager1.AsyncPostBackSourceElementID.EndsWith("$btnMoveCalculationUp") || ScriptManager1.AsyncPostBackSourceElementID.EndsWith("$btnMoveCalculationDown")))
{
//do something on the postback
}
}
foreach (Calculation calc in calculationCollection)
{
AssignedFieldCalculation asCalc = AssignedFieldCalculation.LoadControl(calc);
asCalc.ID = "calc" + calc.UniqueXKey;
pnlFieldCalculations.Controls.Add(asCalc);
foreach (Control ct in asCalc.Controls)
{
if (ct.ID == "btnMoveCalculationDown" || ct.ID == "btnMoveCalculationUp" || ct.ID == "btnRemoveCalculationFromField")
{
ScriptManager1.RegisterAsyncPostBackControl(ct);
}
}
}
}
A few things to note:
You need to make each control ID unique when adding it to the asp:Panel (called pnlFieldCalculations).
The LoadControl method allows you to pass initial arguments