Dynamic controls in an UpdatePanel not always raises asynchronously - c#

I try to create a dynamic table with some textboxes depending on a object List.
Then I add it in an Panel contained in UpdatePanel.
Everything works great, except that some times, the postback is async, and some times, all the page reloads. There is no rule, some times it will work twice before being full postback, and some times more. Can't find a logic with this behaviour.
Here is a piece of my aspx code:
<asp:UpdatePanel ID="udpTableDechets" runat="server" UpdateMode="Conditional">
<ContentTemplate>
<asp:Panel ID="pnlTableDechets" runat="server" />
</ContentTemplate>
</asp:UpdatePanel>
Here is a piece of my code behind:
protected override void OnLoad(EventArgs e)
{
generateTableDechets();
base.OnLoad(e);
}
private void generateTableDechets()
{
Table tbl = new Table { ID = "dechets", ClientIDMode = ClientIDMode.Static };
TableRow trDec = new TableRow();
tbl.Controls.Add(trDec);
TableCell tdDecReel = new TableCell();
trDec.Controls.Add(tdDecReel);
TextBox txtDechet = new TextBox { ID = string.Concat("txtDechet_", product.Nom), ClientIDMode = ClientIDMode.Static, AutoPostBack = true };
txtDechet.TextChanged+=new EventHandler(txtDechet_TextChanged);
tdDecReel.Controls.Add(txtDechet);
pnlTableDechets.Controls.Add(tbl);
}
protected void txtDechet_TextChanged(object sender, EventArgs e)
{
// Get the value, and update the object containing values
// Then update labels in table thanks to another method
}
UPDATE 1
Actually, I tried the same in static, and I have exactly the same behaviour.
<asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional">
<ContentTemplate>
<asp:TextBox runat="server" AutoPostBack="true" OnTextChanged="txt_TextChanged" />
</ContentTemplate>
</asp:UpdatePanel>
Is it normal for you ? Is it a known bug ? Have I forgive something ?
How can I do to be sure every textChanged request will execute asynchronously.
Thank you in advance for your answers
Update 2
The problem seems to occur when I press Enter key or when I hilight the textbox content to replace it.
Solution
I finally did it thanks to the IPostBackEventHandler Interface (see here).
I manage the event manually, and catch it in the RaisePostBackEvent() method. So here, thanks to the control ID passed in parameter, I can do my stuff.
Thank you for your answers

It is a comman issue while creating a website that contain dynamic controls.
If you want to fire event each time you should call code behind from javascript.
Since javascript is executed each time.
ex.
function SaveClick() {
Page.GetPostBackEventReference(objBtnSave);
__doPostBack("objBtnSave", "OnClick");
}
May be this will resolve your problem.

In static, I made it work adding the ChildrenAsTriggers="true" Property, or specifying an AsyncPostBackTrigger on each dynamic control with the TextChanged event.
But this does not work with my dynamic code.

Related

Asp.net ScriptManager history management not working: state is always empty

I'm experimenting with asp.net's UpdatePanel and ScriptManager controls to ajax-enable a page, and I wanted to implement "history" using ScriptManager's AddHistoryPoint() facility.
Here's a fragment of my page:
<asp:ScriptManager runat="server" ID="searchScriptManager" AsyncPostBackTimeout="6000" EnableHistory="true" OnNavigate="searchScriptManager_Navigate"/>
<div class="container">
<asp:UpdatePanel runat="server" ID="updPanelSearch">
...stuff....
</asp:UpdatePanel>
</div>
So, when some button or other control triggers an event in code behind, I add a HistoryPoint as follows:
protected void btnPerformSearch_Click(object sender, EventArgs e)
{
//Do stuff...
searchScriptManager.AddHistoryPoint("actionDone", "search");
}
This seems to work properly:the URL of my page changes and when I press back the Navigate event of the ScriptManager fires correctly. Here's the problem tho. In my handler for the Navigate event the State dictionary is always empty, making it impossible to handle things properly:
protected void searchScriptManager_Navigate(object sender, HistoryEventArgs e)
{
string d = e.State["actionDone"]; //PROBLEM: e.State is always an empty dictionary, d will always be null!!!
if (d == "search")
{
//hide search results...
}
}
What am I doing wrong?

Manually bind a row in a repeater, to avoid losing viewstate data

I'm working on a page that uses a repeater to display a list of custom controls, each containing two dropdown lists.
On a click on the Add control button, the page adds a new row on the repeater, and a click on one of the Delete control buttons embedded in each control removes the relevant control from the repeater.
The delete part seems to work, (setting NamingController.Visible to false), but the add part fails, as once I add the new control, a call to a new repeater.DataBind() loses all viewstate data, preventing the dropdownlists from retrieving the values they had before postback.
Is there a way to manually bind the added control to the repeater without calling a full databind ? Or is there any other way to add a control without losing data ?
Here's some code (I only left what seems relevant, please let me know if you think I forgot to specify something) :
Page.aspx:
<asp:Button ID="addControl" runat="server" Text="Add control" />
<asp:Repeater ID="repeater" runat="server" OnItemDataBound="repeater_ItemDataBound">
<ItemTemplate>
<uc:CustomControlWithDropDownLists ID="custom" runat="server" />
</ItemTemplate>
</asp:Repeater>
Page.aspx.cs:
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
repeater.DataSource = GetDataSource();
repeater.DataBind();
}
protected void Page_Init(object sender, EventArgs e)
{
addControl.Click += (sndr, args) =>
{
// Create the object we want to bind to the repeater
ObjectToBind objectToBind = new ObjectToBind();
// Here is what causes data loss
((IList<ObjectToBind>)repeater.DataSource).Add(objectToBind);
repeater.DataBind();
};
}
protected void repeater_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
// Do some stuff
}
}
CustomControlWithDropDownLists.ascx:
<%-- Some dropdown lists --%>
<asp:Button ID="deleteControl" runat="server" Text="Delete control" />
CustomControlWithDropDownLists.ascx.cs:
protected void Page_Init(object sender, EventArgs e)
{
deleteControl.Click += (sndr, args) =>
{
// ... Delete the control ...
((Button)sndr).NamingContainer.Visible = false;
};
}
You are loosing the data because of View state.
View State losses the data when we are redirecting to the another page.
View state only store the data for the specific page.
The moment you go to another page data is lost by view state.
& there are many chances of losing the data on the same page because of many reasons.
So, the best way to store the data is to use session variable.
Session stores the data even when you are redirecting to the another page.
It is the best way to store the data.
By default it stores the data for 20 minutes.
I hope this will solve your issue.
I had done something similar in the past and found this link from c-sharp corner helpful.
http://www.c-sharpcorner.com/Blogs/10913/add-dynamic-row-using-repeater.aspx
The key element for me if memory servers is using the ViewState object and binding accordingly.
myObject = ViewState["MyData"]; etc.
( I am sorry I don't have access to my code at the moment )

Dynamically added items don't raise events.(ASP.NET ListBox)

My code is:
private void Add_Items()
{
for (int x = 1; x < 53; x++)
{
ListBox1.Items.Add("Item" + x);
ListBox1.DataValueField = "Value" + x;
}
}
None of these items raises SelectedIndexChanged event when clicked.
Please assist.
Make sure autopostback is enabled, like in this examle:
<asp:ListBox ID="listBoxLocation" runat="server" AutoPostBack="True"
OnSelectedIndexChanged="listBoxLocation_SelectedIndexChanged" EnableViewState="True">
<asp:ListItem>1</asp:ListItem>
<asp:ListItem>2</asp:ListItem>
<asp:ListItem>3</asp:ListItem>
</asp:ListBox>
Or to dynamically populate:
Protected void Button1_Click (object sender, System.EventArgs e)
{
ListBox1.Items.Add(new ListItem("Carbon", "C"));
ListBox1.Items.Add(new ListItem("Oxygen", "O"));
}
From: http://msdn.microsoft.com/en-us/library/14atsyf5%28v=vs.85%29.aspx
First.
The ListBox1.DataValueField should not be set to every item. This property sets the field on the data object (each row) to capture the value from. Here is the MSDN link for this property http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.listcontrol.datavaluefield(v=vs.110).aspx.
Next, I am assuming you have all the front end code wired up something like.
<asp:ListBox ID="ListBox1" OnSelectedIndexChanged="ListBox1_SelectedIndexChanged" runat="server"></asp:ListBox>
This has the selected changed event wired up. However for this control to actually Post the data back you need to provide one more attribute. Add
AutoPostBack="true"
To your control as.
<asp:ListBox ID="ListBox1" OnSelectedIndexChanged="ListBox1_SelectedIndexChanged" AutoPostBack="true" runat="server"></asp:ListBox>
This starts the magic. MSDN for AutoPostBack: http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.listcontrol.autopostback(v=vs.110).aspx
Your code is very vague, and im going to make the assumption that your ListBox has not been linked up to a SelectedIndexChanged event in the correct way.
If you are dynamically creating the ListBox link it up as below:
public void initialize()
{
ListBox lb = new ListBox();
lb.SelectedIndexChanged += lb_SelectedIndexChanged;
}
private void lb_SelectedIndexChanged(object sender, EventArgs e)
{
//Do Selected Index Changed Code Here
}
If you have a view/form with the control on, simply make sure that the ListBox's event has been set or naturally it will not trigger.
*EDIT 1
As noted in other answers your control is required to have PostBack set. You should also be checking the PostBack state of your page to ensure you are not redrawing your controls continuously, as this will keep resetting your Dynamically added controls.

Checkbox List evaluating all checkmarks as false

I have a simple checkbox list and I'm using a for statement to retrieve the selected values into one string. This has to be simple, but everything is returning false when it evaluates if it is selected.
ASP Code
<asp:CheckBoxList runat="server" ID="ckblInterests" ClientIDMode="Static" RepeatColumns="2" />
ASP.NET Code:
string interests = "";
for (int i = 0; i < ckblInterests.Items.Count; i++)
{
if (ckblInterests.Items[i].Selected)
{
interests += ckblInterests.Items[i].Value + ", ";
}
}
}
The inside if statement evaluates as false each time it loops through. It does count 10 items in the list correctly. I'm stumped at something so simple. Can someone help me identify what might be causing the if statement to return false?
You have code that's dynamically adding the checkboxes to the list on page load (or some other event). This is resulting in the state of those checkboxes being cleared and re-added on each postback. Your page load should probably have an if(!page.ispostback) around that section so that you aren't clearing the content.
With the Following Code (Mostly yours)
Aspx
<div>
<asp:CheckBoxList runat="server" ID="ckblInterests" ClientIDMode="Static" RepeatColumns="2">
<asp:ListItem>Awesome</asp:ListItem>
<asp:ListItem>Tasty</asp:ListItem>
<asp:ListItem>Terrible</asp:ListItem>
</asp:CheckBoxList>
</div>
<asp:Button runat="server" ID="test" OnClick="test_Click" />
<asp:Label runat="server" ID="label"></asp:Label>
c#
protected void test_Click(object sender, EventArgs e)
{
string interests = "";
for (int i = 0; i < ckblInterests.Items.Count; i++)
{
if (ckblInterests.Items[i].Selected)
{
interests += ckblInterests.Items[i].Value + ", ";
}
}
this.label.Text = interests;
}
I was able to produce the following. This is of course after clicking the button.
Are you binding to a datasource that you have not mentioned?
make sure while binding the checkboxlist in page load you have set this check if (!Page.IsPostBack) { ...bind your data }
this should do the trick
I think you need to check the CHECKED property, not SELECTED.

On Postback, the DataTable data source for a Repeater is empty?

Background
I have a User Control (an .ascx file) which is being dynamically inserting into an asp:PlaceHolder control on a page. That User Control contains an asp:Repeater, which I'm binding to a DataTable.
Theoretically, on the User Control's first load the DataTable is initialized and 3 empty rows are added. A button on the User Control adds additional empty rows to the Repeater, one at a time.
Problem
The issue is that after any PostBack event on the page (namely the button in this example being clicked), the DataTable for the Repeater is empty.
User Control (.ascx)
(simplified)
<asp:TextBox ID="controlOutsideRepeater" runat="server" />
<asp:Repeater ID="myRepeater" runat="server">
<ItemTemplate>
<p><asp:Textbox ID="firstControlInRepeater" runat="server" text='<%# DataBinder.Eval(Container.DataItem, "A") %>' /></p>
<p><asp:Textbox ID="secondControlInRepeater" runat="server" text='<%# DataBinder.Eval(Container.DataItem, "B") %>' /></p>
</ItemTemplate>
</asp:Repeater>
<asp:LinkButton ID="addItemButton" runat="server" Text="Add Item" onclick="addNewItem" />
Code Behind (.ascx.cs)
(also simplified)
public DataTable items {
get {
object i = ViewState["items"];
if (i == null) {
DataTable t = new DataTable();
t.Columns.Add("A");
t.Columns.Add("B");
// add 3 blank items/rows:
t.Rows.Add(t.NewRow());
t.Rows.Add(t.NewRow());
t.Rows.Add(t.NewRow());
ViewState["items"] = t;
return t;
} else {
return (DataTable)i;
}
set { ViewState["items"] = value; }
}
protected void Page_Init(object sender, EventArgs e) {
myRepeater.DataSource = this.items;
myRepeater.DataBind();
}
public void addNewItem(object sender, EventArgs e) {
DataRow r = this.items.NewRow();
this.items.Rows.Add(r);
myRepeater.DataBind();
}
Behavior
The first time the UserControl is loaded, the Repeater contains 3 empty items: good! However, after entering some text in the textboxes both inside and outside the repeater and clicking the "Add Item" LinkButton, the page does a refresh/postback and shows 4 empty items, however the textbox -outside- the Repeater retains it's text. Clicking the "Add Item" LinkButton again also performs a postback and still shows 4 empty items, yet the TextBox outside the Repeater again retains it's text.
My Crazy Guess
I've tried wrapping the Repeater databinding in a (!Page.IsPostBack), but this prevented the Repeater from -ever- being bound, as the UserControl is only programmatically added to the page after a PostBack (a button on the Page adds the UserControl on a click, and then the Page checks each PostBack to see if there should be a user control present and re-adds it to the Page if needed). So I'm guessing there's a problem with the Page re-creating the User Control on every PostBack, but can't explain why the TextBox outside the Repeater would retain it's value, and why the ViewState doesn't seem to remember my item (on each postback ViewState["items"] is null and gets re-built within the getter).
HELP!
The problem is you are data binding every single request when really you only want to data bind on the first request. Since you don't data bind on the first page load, you will have to check if you are data bound in a way other than !Page.IsPostBack. You could add a property to your user control to handle this and then check against that every page load / page init.
Update: With more details from comments
I see your AddItem() now. I've had problems using viewstate this way though I'm not entirely sure why. I've had to do it more like the following:
public void addNewItem(object sender, EventArgs e) {
DataTable theItems = this.items;
DataRow r = theItems.NewRow()
theItems.Rows.Add(r);
this.items = theItems
myRepeater.DataBind(); //I'm not sure if this belongs here because of the reasons said before
}

Categories