This question already has an answer here:
ASP.NET Custom user control to add dynamically
(1 answer)
Closed 5 years ago.
I have a user control(UserControl1) on my page and one Add more button.Clicking on Add More Button add the same user control again.When i click on user control first time,it adds second User Control,but on second time it doesnot add other.I think the control gets lost.
I am adding a control like this on link button click:-
protected void lnkadd_Click(object sender, EventArgs e)
{
HtmlTableCell tCell = new HtmlTableCell();
UserControl uc = (UserControl)Page.LoadControl("~/Controls/DirectionalPricingCtrl.ascx");
tCell.Controls.Add(uc);
tablerow.Cells.Add(tCell);
}
I think i am doing something wrong and i should add user controls in respect of page life cycle,but how?
Can somebody please guide me or provide some useful links?
What approach i should follow when i have to add a user control each time when i click on a Add button and then later retrieve all the values and save it to DB?
I prefer javascript / ajax solution but I think there is nothing wrong with your code.
I did small example. Here is same solution as you have. Advantage is that control is loaded only in case of click.
public partial class Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void btnLink_Click(object sender, EventArgs e)
{
var uc = (UserControl)Page.LoadControl("~/WebUserControl1.ascx");
pnl.Controls.Add(uc);
}
}
Here is example where user control is loaded in Page_Load event and in case of click (btnLink_Click) user control is added to panel. It works same as your solution but user control can be loaded (processed in memory not redered) even if is not needed.
public partial class Default : System.Web.UI.Page
{
UserControl uc;
protected void Page_Load(object sender, EventArgs e)
{
if(IsPostBack) // This condition is not needed but we know that click is always postback
uc = (UserControl)Page.LoadControl("~/WebUserControl1.ascx");
}
protected void btnLink_Click(object sender, EventArgs e)
{
pnl.Controls.Add(uc);
}
}
Here is solution which I prefer, it's based on visible property. In case that user control is not visible it's not rendered to output. Sure it's not very practival in case of table with lot of cells as a control container instead of panel.
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="WebApplication1.Default" %>
<%# Register Src="~/WebUserControl1.ascx" TagName="ucrCtrl" TagPrefix="ctr" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Button ID="btnLink" runat="server" Text="Add" OnClick="btnLink_Click" />
<asp:Panel runat="server" ID="pnl">
<ctr:ucrCtrl runat="server" ID="usrCtrl" Visible="false" />
</asp:Panel>
</div>
</form>
</body>
</html>
namespace WebApplication1
{
public partial class Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void btnLink_Click(object sender, EventArgs e)
{
usrCtrl.Visible = true;
}
}
}
Your code to add controls programmatically is right. Try doing,
tablerow.Cells.Add(tCell);
instead of adding it to the controls. this looks like a control rendering issue.
You thought right, your controls get lost due to a missing procedure which should handles post backs. Whenever you define a variable of a control-class such as HtmlTableCell, you should handle all procedures required for saving and loading data from posted page by yourself, instead of using predefined ones offered by asp.net run-time engine.
It's better to save and load those controls in some storage places during Postback such as ViewState on each post-back on PageLoad, then, handle adding/removing extra controls in lnkadd_Click method.
Related
I need to add a UserControl dynamicaaly to a Panel on a page. The UserControl has a Repeater with the ID of ARepeater. I load and add the UC on Page_Init. I examine the value of ARepeater in Init, Load, and PreRender events of UC but ARepeater is always null.
protected Page_Init(object sender, EventArgs e)
{
var list = (NameList)Page.LoadControl(typeof(NameList), new object[1] { (int)Type });
Panel1.Controls.Add(list);
}
The NameList.ascx
<%# Control Language="C#" AutoEventWireup="true" CodeBehind="NameList.ascx.cs" Inherits="MyCompant.Controls.BannersList" %>
<asp:Repeater ID="ARepeater" runat="server">
<ItemTemplate>
</ItemTemplate>
</asp:Repeater>
What I am doing wrong?
First of all, you do not need to be in Page_Init to work with dynamic controls. Page_Load is just fine. But in order to fill the Repeater you can create a property in the UserControl
public partial class WebUserControl1 : System.Web.UI.UserControl
{
public Repeater _ARepeater
{
get
{
return ARepeater;
}
set
{
ARepeater = value;
}
}
protected void Page_Load(object sender, EventArgs e)
{
}
Then you can access it from the page using the UserControl.
protected void Page_Load(object sender, EventArgs e)
{
var list = (WebUserControl1)LoadControl("~/WebUserControl1.ascx");
list.ID = "MyUserControl";
Panel1.Controls.Add(list);
list._ARepeater.DataSource = source;
list._ARepeater.DataBind();
}
Or use FindControl
var _ARepeater = (Repeater)Panel1.FindControl("MyUserControl").FindControl("ARepeater");
_ARepeater.DataSource = dt;
_ARepeater.DataBind();
You probably won't like this answer, but the overload for Page.LoadControl that allows for specifying the control's type and adding constructor arguments doesn't bind the ascx to the code-behind, and all associated child-controls will end up being null.
In the past, I've worked around this by adding another method for setting dependencies after constructing the user control, but it's not an ideal solution.
That said you aren't doing anything wrong. Binding will work properly if you use Page.LoadControl("~/path/to/mycontrol.ascx"), but you won't have constructor injection.
I believe the issue lies with the fact that the backing class doesn't actually have a relationship with the front-end page, except through the page directive that specifies it as the code-behind class. Nothing stops multiple different front-ends using the same class as it's code-behind, so loading by Type makes it either very difficult or outright impossible to determine what the correct ascx to bind would be.
Do web controls ever appear like you are changing their values but actually retain the previous value?
I created a pop-up modal for users to edit an item. When the user clicks edit on an item on the main page, the following sequence happens:
The item's ID is passed to the Page_Load event of the modal page, and is used to populate the page control's with the item's data.
The user changes a value in a control. Ex: Changes text in a TextBox contol.
The user clicks save, triggering the Click event which creates a DataTransferObject with the values in the textboxes, which will be stored.
However, on step 3, the control's new value (TextBox.Text) still holds the value that it orginially had, not the value the user put in.
Add.aspx:
<%# MasterType VirtualPath="../MasterPages/Popup.Master" %>
<asp:Content ID="BodyContent" runat="server" ContentPlaceHolderID="MainContent">
<asp:TextBox ID="TextBoxDescription" runat="server"></asp:TextBox>
<telerik:RadButton ID="btnSave" runat="server" Text="Save" OnClick="btnSave_Click"/>
</asp:Content>
Add.aspx.cs
//Cannot access the new values here
protected void btnSave_Click(object sender, EventArgs e)
{
//This will print the new text on Create, but the old text on Edit
System.Diagnostics.Debug.WriteLine(TextBoxDescription.Text);
}
//works properly
protected void Page_Load(object sender, EventArgs e)
{
objIDParam = Convert.ToInt64(Request.QueryString["ObjectID"]);
editMode = (objIDParam != 0) ? true : false;
if(editMode)
PopulateFields(objID);
}
//works properly
private void PopulateFields(long objID)
{
MyObject obj = GetObjectByID(objID);
TextBoxDescription.Text = obj.Description;
}
It is worth noting that this popup page is used for both creating items AND editing items. Create works fine (i.e. The item isn't saved with all blanks, but rather the user input). Editing an item will properly pull all that data back in, and let the user edit the fields, however I can't access the changed values in my code.
You need to check for IsPostBack in the Page_Load method.
The Page_Load gets called before the btnSave_Click method, so the TextBoxDescription.Text is getting reset to obj.Description before the btn_Save method runs.
Try returning out of Page_Load if you're posting back:
protected void Page_Load(object sender, EventArgs e)
{
if (IsPostBack)
return;
objIDParam = Convert.ToInt64(Request.QueryString["ObjectID"]);
editMode = (objIDParam != 0) ? true : false;
if(editMode)
PopulateFields(objID);
}
Have a look at ASP.NET Page Life Cycle Overview for more info.
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?
I have a problem that I believe is a session state issue, but I'm at a loss to figure out what's wrong. I have a sample project to illustrate the problem. (Code below) I have 2 buttons. Each populates a List with some unique data and then uses that data to add a row to a table. The row contains text boxes so that the user can edit the data. (For my sample, there's no update button to persist the data.) To reproduce the problem in VS2010, create a new "ASP.NET Web Application" project and copy/paste the aspx code and the c# code-behind into Default.aspx, then run the application.
Press the DataSet 1 button and the grid should populate with 1 row.
Edit the data in one of the text boxes and tab off of the text box. (The newly entered text should remian, and the font should be blue. This is what I want to happen.)
Now click either of the DataSet buttons to reset the List and refresh the table.
Edit the data in one of the text boxes and tab off the text box. (Immediately, the text in the box refreshes back to its original value. This only happens once, though. If you edit either text box now, it will work normally.)
This is repeatable... the first edit after pressing the DataSet buttons a 2nd, 3rd, etc. time gets reset back to the original value. And I can't figure out why.
<%# Page Title="Home Page" Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true"
CodeBehind="Default.aspx.cs" Inherits="DebugPostbackIssue._Default" %>
<asp:Content ID="HeaderContent" runat="server" ContentPlaceHolderID="HeadContent">
</asp:Content>
<asp:Content ID="BodyContent" runat="server" ContentPlaceHolderID="MainContent">
<h2>
Welcome to ASP.NET!
</h2>
<p>
Populate the table with DataSet #1:<asp:Button runat="server" ID="btnDS1" Text="Dataset 1" OnClick="btnDS1_Click" />
</p>
<p>
Populate the table with DataSet #2:<asp:Button runat="server" ID="btnDS2" Text="Dataset 2" OnClick="btnDS2_Click" />
</p>
<p>
<asp:Table runat="server" ID="tblData">
<asp:TableHeaderRow runat="server" ID="thrData">
<asp:TableHeaderCell Scope="Column" Text="Column 1"></asp:TableHeaderCell>
<asp:TableHeaderCell Scope="Column" Text="Column 2"></asp:TableHeaderCell>
</asp:TableHeaderRow>
</asp:Table>
</p>
</asp:Content>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace DebugPostbackIssue
{
public partial class _Default : System.Web.UI.Page
{
private List<string> _MyData = new List<string>();
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Page_Init(object sender, EventArgs e)
{
LoadSessionData();
GenerateGrid(false);
}
protected void btnDS1_Click(object sender, EventArgs e)
{
_MyData = new List<string>();
_MyData.Add("111");
_MyData.Add("aaa");
SaveSessionData();
GenerateGrid(true);
}
protected void btnDS2_Click(object sender, EventArgs e)
{
_MyData = new List<string>();
_MyData.Add("222");
_MyData.Add("bbb");
SaveSessionData();
GenerateGrid(true);
}
private void SaveSessionData()
{
Session["MyData"] = _MyData;
}
private void LoadSessionData()
{
if (Session["MyData"] != null)
_MyData = (List<string>)Session["MyData"];
else
_MyData = new List<string>();
}
private void GenerateGrid(bool ClearData)
{
if (ClearData)
while (tblData.Rows.Count > 1)
tblData.Rows.Remove(tblData.Rows[tblData.Rows.Count - 1]);
TableRow tr = new TableRow();
foreach (string s in _MyData)
{
TableCell tc = new TableCell();
TextBox txtBox = new TextBox();
txtBox.Text = s;
txtBox.Attributes.Add("OriginalValue", s);
txtBox.TextChanged += new EventHandler(txtBox_TextChanged);
txtBox.AutoPostBack = true;
tc.Controls.Add(txtBox);
tr.Cells.Add(tc);
}
if (tr.Cells.Count > 0)
tblData.Rows.Add(tr);
}
void txtBox_TextChanged(object sender, EventArgs e)
{
TextBox Sender = (TextBox)sender;
if (Sender.Text == Sender.Attributes["OriginalValue"])
Sender.ForeColor = System.Drawing.Color.Black;
else
Sender.ForeColor = System.Drawing.Color.Blue;
}
}
}
Hi I took some time off of my work to compile your code, and I figured it out, just change your textbox change to the following :
void txtBox_TextChanged(object sender, EventArgs e)
{
TextBox Sender = (TextBox)sender;
if (Sender.Text == Sender.Attributes["OriginalValue"])
Sender.ForeColor = System.Drawing.Color.Black;
else
{
Sender.ForeColor = System.Drawing.Color.Blue;
if (Session["MyData"] != null)
{
List<string> _ss = (List<string>)Session["MyData"];
//_ss.Find(a => a == Sender.Attributes["OriginalValue"]);
_ss.Remove(Sender.Attributes["OriginalValue"]);
_ss.Add(Sender.Text);
}
}
}
ur welcome!
Try creating a datatable. I would, on "event" copy your asp table content to a datatable, then when you get the servers response add that to the datatable. Then copy the datatable back to your asp table, and repeat... Datatables can be used like variables.
Or try using a cookie.
Try changing...
protected void Page_Init(object sender, EventArgs e)
{
LoadSessionData();
GenerateGrid(false);
}
To...
protected override void OnLoadComplete(EventArgs e)
{
LoadSessionData();
GenerateGrid(false);
}
Based on your description I believe that your value is getting reset because of how the page life cycle works in ASP.NET & that is the page_init is getting called before your event due to ASP.NET quirkiness. Above code is how I work around it, I'm sure there's other ways too.
Okay, I have it working, now. Wizpert's answer pointed me in the right direction... Upon discovering that the TextChanged event did not fire during the times when the value was erroneously being reset to the original value, it occurred to me that during the Click events I was calling GenerateGrid(true). This forced the removal of the existing rows and the addition of new rows. (Removing & adding the dynamic controls at that point in the life cycle must be interfering with the TextChange event handler.) Since the Click event fires after Page Init and after Page Load, the state values were already written to the text boxes and I was overwriting them. But the 2nd text box edit did not force GenerateGrid(true) to be called so the state values were not overwritten any more.
If this sounds confusing, I apologize. I'm still wrapping my head around this. But suffice to say that I had to change my GenerateGrid method to reuse any existing rows and not delete them. (If they don't exist, like when GenerateGrid is called from Page Init, then they are added.) So this was a page lifecycle issue after all.
Thank you.
I have a dynamic table with some textboxes (also dynamic) and some buttons which do postback onclick.
How can I make the page remember what text was entered in the boxes after postback, after clicking a button?
You have to create controls in a tymer click event.For that Create a new user control. Add public Properties in it for adding how much controls u have to add. And in Web user control Page INit and Page_load event Add the required number of controls. Hope this will work.
//IN web user control aspx page add a place holder in which u add your dynamic controls
<%# Control Language="C#" AutoEventWireup="true" CodeFile="WebUserControl.ascx.cs" Inherits="WebUserControl" %>
<asp:PlaceHolder runat="server" ID="mycontrol"/>
// WEb User Control Code Behind
// Create public properties
public int totalnoOfcontrols
{
get;
set;
}
protected void Page_Load(object sender, EventArgs e)
{
if (IsPostBack)
{
// save values here
}
}
protected void Page_Init(object sender, EventArgs e)
{
// create dynamic controls here
TextBox t = new TextBox();
t.Text = "";
t.ID = "myTxt";
mycontrol.Controls.Add(t);
}
You have to use Page_Init/Load event handler to create controls runtime (dynamically).
For that you can use the ViewState
string data = ViewState["myData"];
ViewState["myData"] = data;