MSDN says that create Dynamic Controls in PreInit Event of Page Life Cycle.
http://msdn.microsoft.com/en-us/library/ms178472.aspx
Why?
What advantage is derived by creating in PreInit Event?.
I have seen code where developers are creating dynamic controls in the Page_Load Method?
If there any difference?.
Regards
Page_Load works fine if you don't need to worry about saving the controls' ViewState across postbacks, but if you need to persist it, the Load stage is not where you should add these controls.
Dynamic controls must exist within the page's control hierarchy before the ViewState is loaded. There's only one stage before Load View State - Initialization. That means, if you want your dynamic controls to persist view state you must add them to the control hierarchy in the page's Init event.
https://web.archive.org/web/20210302172017/https://aspnet.4guysfromrolla.com/demos/printPage.aspx?path=/articles/092904-1.aspx
But mind that you cannot access the ViewState in Init event because it's yet not loaded. So you need to use a different persistence medium to store variables across postbacks(like Session) if required.
This reply might be late for the original poster but I thought it might help some other people.
If your application/ website doesn't use master page, it's best to create controls at Page_PreInit event. But if you use master page and want to create controls on content pages at run time Page_Init is the ideal event.
You can also create controls on Page_Load but bear in mind page load is fired after View State is loaded.
Tim,
Thanks for the reply.
I did a small experiment in which I am creating and adding a TextBox control dynamically in Page_Load method.
In the Postback click event of Button on the page,I am trying to get the value of the TextBox's Text Property.
I am able to get the value in the click event of the Button when the control is dynamically added in the Page_Load event and not the OnPreInt method.
I think that the Text value is retained in the ViewState even though the control is been added in Page_Load method.
following the code:
<div>
<p>
<asp:Label ID="lbl" runat="server" />
</p>
<p>
<asp:PlaceHolder ID="plcHolder" runat="server"></asp:PlaceHolder>
</p>
<p>
<asp:Button ID="btn" runat="server" Text="Click" OnClick="btn_Click" />
</p>
</div>
private void _createTextBox()
{
TextBox textBox = new TextBox();
textBox.ID = "txtBox";
textBox.Width = 250;
textBox.ReadOnly = false;
plcHolder.Controls.Add(textBox);
}
protected void Page_Load(object sender, EventArgs e)
{
_createTextBox();
if (!this.IsPostBack)
{
Control ctrl = plcHolder.FindControl("txtBox");
if (ctrl != null)
{
TextBox txtBox = ctrl as TextBox;
txtBox.Text = DateTime.Now.ToString();
}
}
}
protected void btn_Click(object sender, EventArgs e)
{
Control ctrl = plcHolder.FindControl("txtBox");
if (ctrl != null)
{
TextBox txtBox = ctrl as TextBox;
lbl.Text = txtBox.Text;
}
}
Kindly let me know is this correct or what am i doing wrong?
Related
Disclaimer: I have read the ASP.net page life cycle, and I've also read several good articles pertaining to dynamic controls, but I must be missing something.
Background: I am currently working on a website which needs to create a lot of dynamic content based on the user's input. I understand that, in order for dynamic controls to persist and for their events to wire up correctly, I need to re-create those dynamic controls on every page post-back.
Because of the nature of my project, my code doesn't know WHAT controls to create unless it has some information about what the user selected. I'm storing the user's choices in the ViewState, which is unavailable in Page_Init because it has not yet loaded. Consequently, I have to wait until Page_PreLoad or Page_Load to read the ViewState and then re-create my dynamic controls.
The part I don't understand: When I try re-creating my controls during Page_Load, the controls persist but the associated events don't seem to fire. For example, clicking on a LinkButton I created does not fire the method that I wired to its Click event, even though the button itself does persist.
A strange solution that I discovered by accident is that I can instead re-create my controls during Page_PreLoad and then the events fire correctly.
My question (or questions, rather): Why does my problem appear to go away by re-creating my controls during Page_PreLoad instead of Page_Load? Is this a safe practice? I've never seen any other code that used Page_PreLoad, which makes me wary. This solution is working for me, but are there any pitfalls I might be missing? Am I unknowingly setting myself up for failure later on?
My code, where LoadDocument() is a method that creates controls and stuffs them into a static Panel control:
protected void Page_PreLoad(object sender, EventArgs e)
{
if (ViewState["xmlfilename"] != null)
{
LoadDocument(ViewState["xmlfilename"].ToString());
}
}
Your events are processed during the ProcessPostData. Which control triggered the postback, too is post data. If your control does not exist at that time, it will not receive the event.
I agree Init would be too early, and Load too late.
What you need to do here is create these controls as soon as your view state is loaded.
There is no event for this in the Page life cycle. However all the functions being virtual, you can override the functions called in between.
The best place to load such controls that depend on values stored in the ViewState is the LoadViewState function.
Override this function.
Remember to call base.LoadViewState at the very start.
Create your controls depending on the ViewState values.
Now all your controls events should fire properly.
Probably you read one of my answers on this topic:
https://stackoverflow.com/a/11127064/1268570
https://stackoverflow.com/a/11061945/1268570
https://stackoverflow.com/a/11167765/1268570
I can tell you that I have code on production using the PreLoad event and it has worked fine
But for new development I am using the Init event, Why? Because it is the Microsoft recommendation and therefore it can be considered as an standard, and the technical benefits such the automatic load of the ViewState, theme support, and the most important (from my point of view), the dynamic controls events are sync with the static controls.
Your concern is right, in the Init event the ViewState has not been loaded yet, but that doesn't stop you to access the Form collection
I created a page for learning purpose where I'm creating dynamic controls on demand, and I'm doing it in the Init event. I'm creating TextBoxes and on each post they raise their TextChanged event when the text is changed.
NOTE: before continue I would like to remind you that the ViewState is loaded matching the control's ID's, that's why it's a good practice to re-create always the dynamic controls using the same ID
This is the code:
ASPX
<asp:HiddenField runat="server" ID="numberOfDynamicControls" Value="0" />
<asp:Panel runat="server" ID="myPanel">
</asp:Panel>
<asp:Button Text="Add Control" runat="server" ID="addControl" OnClick="addControl_Click" />
<asp:Label ID="lblMessage" runat="server" />
ASPX code behind
protected void Page_Init(object sender, EventArgs e)
{
this.CreateDynamicControls();
}
protected void addControl_Click(object sender, EventArgs e)
{
var n = int.Parse(this.numberOfDynamicControls.Value);
n++;
this.numberOfDynamicControls.Value = n.ToString();
this.myPanel.Controls.Add(this.CreateTextbox(n));
}
private void CreateDynamicControls()
{
int n = 0;
if (!string.IsNullOrWhiteSpace(this.Request.Form["numberOfDynamicControls"]))
{
n = int.Parse(this.Request.Form["numberOfDynamicControls"]);
}
for (int i = 0; i < n; i++)
{
var t = this.CreateTextbox(i + 1);
t.TextChanged += (x, y) => this.lblMessage.Text += "<br/>" + (x as TextBox).ID + " " + (x as TextBox).Text;
this.myPanel.Controls.Add(t);
}
}
private TextBox CreateTextbox(int index)
{
var t = new TextBox { ID = "myTextbox" + index.ToString(), Text = "de" };
return t;
}
I am using UpdatePanel and loading dynamically the content:
<asp:UpdatePanel runat="server" ID="updatePanel" UpdateMode="Conditional">
<ContentTemplate>
<div id="dashboard-main">
<div id="dashboard-content">
<div class="d-content">
<asp:PlaceHolder runat="server" ID="ContentPanel">
</asp:PlaceHolder>
</div>
</div>
<div id="sub-content">
<ul id="nav-02">
<li class="current">
<asp:LinkButton CommandArgument="Stuff" OnClick="LoadMiniDash" runat="server" ID="myStuff">
My Stuff</asp:LinkButton></li>....
What I do is once the LinkButton is pressed, I load a control to the ContentTemplate:
protected void LoadMiniDash(object sender, EventArgs e)
{
string miniDashName = ((LinkButton)sender).CommandArgument;
Control control = Page.LoadControl("~/controls/Dashboard" + miniDashName + ".ascx");
ContentPanel.Controls.Clear();
ContentPanel.Controls.Add(control);
updatePanel.Update();
}
My problem is, that inside this newly loaded control, there is another Button. once clicked, the updatepanel gets emptied and not postback to the control is made. (it does however do a post to the page itself async. but i see the control won't reach its own code once clicked).
what can be the problem?
thanks
I just tried to rewrite your question. When you load your userControl it appears in the updatePanel. When you hit the button in the userControl, your updatePanel gets updated - and your userControl "disappears".
Imo it disappears because you do not reload your userControl. I tried the following snippet:
protected void btnLoadControl_Click(object sender, EventArgs e)
{
/// save the name of the userControl to load in a SessionVar
Session["userControl2Load"] = "ucDemo.ascx";
Control control = LoadControl("ucDemo.ascx");
phDemo.Controls.Clear();
phDemo.Controls.Add(control);
upDemo.Update();
}
/// reload your userControl on every init of the updatePanel
/// when the sessionVar is set
protected void upDemo_Init(object sender, EventArgs e)
{
if (Session["userControl2Load"] != null)
{
string controlName = Session["userControl2Load"].ToString();
Control control = LoadControl(controlName);
phDemo.Controls.Clear();
phDemo.Controls.Add(control);
}
Debug.WriteLine("upDemo Init");
}
it worked for me. Let me know if it work for you too. hth
I have faced similar issue.. and you know solution to this problem is very simple..
Logic is .. If you have one or more update panel in the same page then properties of all update panel should be same such as
Update Mode : Always
EnableViewState : true(if you are using it)
For example you have 3 update panel in the same asp.net ajax page with following id
UpdatePanel1
UpdatePanel2
UpdatePanel3
In the Update Panel 1 (UpdatePanel1) you have a button which specific a record from the gridview. On the click on the button in the update panel1, FormView in the UpdatePanel2 should display the corresponding values.
To achieve this, you need to specific Update Panel Properties of (UpdatePanel1, UpdatePanel2) as
ChildrenAsTrigger : True
EnableViewState : true
RenderMode : Block/Inline
UpdateMode : Always
Visible : true
Doing above activity values in the UpdatePanel3 is changed or you can say it's not depending on the UpdatePanel1 or UpdatePanel2
If you find it useful, please mark it as your answer else let me know...
There's tons of info on the web about the ASP.NET life cycle, but i can't seem to figure out when to dynamically add controls to the page.
In general there are two situations; an aspx page with a masterpage, and one without. The book i'm currently reading (70-515 self prep) says to add controls to a page without a master page in the preinit eventhandler. To dynamically add controls to a contentpage, i should place that logic in the init eventhandler.
According to MSDN (http://msdn.microsoft.com/en-us/library/ms178472.aspx) I should create or recreate dynamic controls in the preinit eventhandler, and only read or initialize properties of controls in the init eventhandler (which makes most sense to me). Googling around I see lots of people using the init eventhandler to add controls.
So, i'm a little bit lost here - what is the correct way? And when using the preinit eventhandler, how could you add controls to your page when all controls are null? For instance, when you need to add a dynamically created textbox to a panel control?
Kind regards,
Unless you need to play around with setting control properties prior to tracking ViewState, I would personally go ahead and place my dynamic control addition logic in the OnInit event.
If you really want to dynamically add a control during the PreInit (when using master page) you can always do something like this:
protected override void OnPreInit(EventArgs e)
{
base.OnPreInit(e);
TextBox textBox = new TextBox();
textBox.Text = "Dynamic TextBox";
textBox.Width = 100;
textBox.ReadOnly = false;
var master = this.Master;
plcHolder.Controls.Add(textBox);
textBox.ApplyStyleSheetSkin(this.Page);
}
accessing the "Master" property would instantiate the controls
and it should work, but you get nested master pages scenarios (this.Master.Master ...), update panels and so on.
This might be relevant and helpful: http://weblogs.asp.net/ysolodkyy/archive/2007/10/09/master-page-and-preinit.aspx
Moreover, one reason I can think of (besides following the defined page lifecycle) MS recommends that we place all the logic for dynamic control creation in the Preinit event so we can take advantage of the theme service, which will apply all available skin properties automatically for us, before the Init event takes place.
Say your markup looks something like that:
<%# Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Trace="true" Inherits="_Default" Theme="Test" %>
...
<form id="form1" runat="server">
<div>
<p>
<asp:TextBox ID="TextBox1" runat="server" TextMode="Password" Text="Control TextBox"></asp:TextBox>
</p>
<p>
<asp:PlaceHolder ID="plcHolder" runat="server"></asp:PlaceHolder>
</p>
</div>
</form>...
and you have a skin like this:
<asp:TextBox runat="server" BackColor="Yellow" Wrap="false" Text="Skin property!" > </asp:TextBox>
Just add this to your code behind:
private TextBox tb1;
protected override void OnPreInit(EventArgs e)
{
base.OnPreInit(e);
tb1 = new TextBox();
tb1.Text = "PreInit Dynamic TextBox";
Trace.Write(String.Format("tb1 Wrap Property-> {0}",tb1.Wrap));
Trace.Write(String.Format("tb1 Text Property-> {0}", tb1.Text));
Trace.Write("Add tb1 to the placeholder.");
plcHolder.Controls.Add(tb1);
Trace.Write(String.Format("tb1 Wrap Property-> {0}", tb1.Wrap));
Trace.Write(String.Format("tb1 Text Property-> {0}", tb1.Text));
}
protected override void OnInit(EventArgs e)
{
Trace.Write(String.Format("tb1 Wrap Property-> {0}", tb1.Wrap));
Trace.Write(String.Format("tb1 Text Property-> {0}", tb1.Text));
base.OnInit(e);
}
protected void Page_Load(object sender, EventArgs e)
{
Trace.Write(String.Format("tb1 Wrap Property-> {0}", tb1.Wrap));
Trace.Write(String.Format("tb1 Text Property-> {0}", tb1.Text));
}
You will notice how before going into the Init event all skin properties are already applied to the dynamically created textbox :)
The PreInit event was new to me, but I guess it makes sense, so that you have an intermediary step in between the loading of controls and viewstate load to do additional work. We've used init event to load dynamic controls and this has always worked for us with no issues. I think you'll be OK with either, but if MS recommends PreInit, I'd say go that route. THis way, in Init, you can do any additional work you may need and separate out the routine that creates the UI vs. the routine that may update it before viewstate load.
HTH.
I´ve read most posts here but i can´t figure out why the "CheckedChanged" Event is not firing. Here is my situation.
I´m using a Repeater to generate Items out of a Database. Each ReapeaterItem should include an UpdatePanel, because i have to Update the Controls inside the UpdatePanel and do not want to reload the complete page. Inside these dynamically generated UpdatePanels (each RepeaterItem has one) i´m adding up to three Checkboxes dynamically (based on the Database). These Checkboxes need to fire the "CheckedChanged" event, because on some conditions i want to enable/disable/check/uncheck Checkbox1, 2 or 3 based on business logic. ... Hope you got this so far. I´m adding all Controls and have the EventHandler Added. But the generated Code does not reflect the Event Handler. I tried OnItemDataBound, OnItemCreated, PreRender, ... Events to add the Eventhandler too, but i was not able to find the CheckBox-Control with the ID.
I´m totally lost with this and on the way to use Buttons instead of Checkboxes. From what i read so far is that with Buttons i can use the CommandName from the Button and the ItemCommand-Event from the Repeater to get a workaround, but then i need to reflect the "Check" on the Page in some way.
btw, every Repeater (8) sits inside an ajaxtoolkit-accordion control.
Here i give you some Code:
aspx-Page
<asp:Repeater ID="RepeaterAccordionPane2" runat="server">
<ItemTemplate>
HTML Stuff<%# DataBinder.Eval(Container.DataItem, "Header")%>HTML Stuff<%# DataBinder.Eval(Container.DataItem, "Beschreibung")%></td>
<asp:UpdatePanel ID="UpdatePanel2" runat="server" UpdateMode=Conditional>
<ContentTemplate>
</ContentTemplate>
</asp:UpdatePanel>
HTML Stuff
</ItemTemplate>
</asp:Repeater>
Here is the Page_Load Part
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
dvAlleArtikel = new System.Data.DataView(...Database...);
[... some other code here ...]
RepeaterAccordionPane2.DataSource = dvAlleArtikel;
//RepeaterAccordionPane2.ItemCreated +=new RepeaterItemEventHandler(RepeaterAccordionPane2_ItemCreated);
//RepeaterAccordionPane2.PreRender +=new EventHandler(RepeaterAccordionPane2_PreRender);
RepeaterAccordionPane2.DataBind();
int nUpdatePanelIndex = 0;
foreach (Control crInRepeater in RepeaterAccordionPane2.Controls)
{
if (crInRepeater.GetType() == typeof(RepeaterItem))
{
foreach (Control crInRepeaterItem in crInRepeater.Controls)
{
if (crInRepeaterItem.GetType() == typeof(UpdatePanel))
{
LiteralControl litTabelleBeginn = new LiteralControl("<table width=\"100%\" border=\"0\" cellspacing=\"0\" cellpadding=\"2\">");
((UpdatePanel)crInRepeaterItem).ContentTemplateContainer.Controls.Add(litTabelleBeginn);
if (dvAlleArtikel[nUpdatePanelIndex]["ArtNr1"].ToString() != "0")
{
CheckBox CheckBox1 = new CheckBox();
CheckBox1.ID = dvAlleArtikel[nUpdatePanelIndex]["ArtNr1"].ToString();
CheckBox1.Text = (dvAlleArtikel[nUpdatePanelIndex]["CheckBoxLbl1"].ToString() == "" ? "leer" : dvAlleArtikel[nUpdatePanelIndex]["CheckBoxLbl1"].ToString());
CheckBox1.AutoPostBack = true;
CheckBox1.CheckedChanged +=new EventHandler(CheckBox1_CheckedChanged);
LiteralControl litNeueTabellenZeileBeginn = new LiteralControl("<tr><td width=10><img src=\"images/helper/spacer.gif\" width=\"10\"></td><td height=\"20\">");
LiteralControl litNeueTabellenZeileEnde = new LiteralControl("</td><td width=\"100\" height=\"20\">" + dvAlleArtikel[nUpdatePanelIndex]["ArtPrice1"].ToString() + " € </td></tr>");
((UpdatePanel)crInRepeaterItem).ContentTemplateContainer.Controls.Add(litNeueTabellenZeileBeginn);
((UpdatePanel)crInRepeaterItem).ContentTemplateContainer.Controls.Add(CheckBox1);
((UpdatePanel)crInRepeaterItem).ContentTemplateContainer.Controls.Add(litNeueTabellenZeileEnde);
}
[... some other code here...]
LiteralControl litTabelleEnde = new LiteralControl("</table>");
((UpdatePanel)crInRepeaterItem).ContentTemplateContainer.Controls.Add(litTabelleEnde);
nUpdatePanelIndex++;
}
}
}
}
This code is never reached:
protected void CheckBox1_CheckedChanged(object sender, EventArgs e)
{
int foo = 0;
}
This is the CheckBox-Code generated:
<input id="AccordionPane2_content_RepeaterAccordionPane2_ctl00_6200" type="checkbox" name="AccordionPane2_content$RepeaterAccordionPane2$ctl00$6200" onclick="javascript:setTimeout('__doPostBack(\'AccordionPane2_content$RepeaterAccordionPane2$ctl00$6200\',\'\')', 0)" />
The Event is generated, but when i click the Checkbox all Content in the UpdatePanel is gone and the CheckedChanged-EventHandler is not fired.
What am i doing wrong?
Thanks to all advice, i´m really stuck.
mk
The first time the page loads you are adding all the checkboxes to the Controls collection, and they get rendered. When you do a postback (through the CheckBox's AutoPostBack) you have a check if(!IsPostBack) that doesn't allow the checkboxes to be added to the Controls collection on the postback. Because of that, you won't see the controls and the page, and when the page lifecycle tries to call the events (which occurs AFTER Page_Load), the controls that created the events are no longer there.
You will need to refactor your Page_Load method so it does two things - 1, regardless of the value of IsPostBack bind the repeaters and create the dynamic controls. 2, if IsPostBack==false, i.e., an initial load, then set the values of the dynamic controls. you don't want to set the values of the dynamic controls when IsPostBack==true because then you will lose the values the user entered.
also, just a note:
if (crInRepeater.GetType() == typeof(RepeaterItem))
can be rewritten as:
if (crInRepeater is RepeaterItem)
I read about the dynamic control creation in ASP.NET this piece of text:
...When using dynamic controls, you
must remember that they will exist
only until the next postback. ASP.NET
will not re-create a dynamically added
control. If you need to re-create a
control multiple times, you should
perform the control creation in the
Page.Load event handler. This has the
additional benefit of allowing you to
use view state with your dynamic
control. Even though view state is
normally restored before the Page.Load
event, if you create a control in the
handler for the Page.Load event,
ASP.NET will apply any view state
information that it has after the
Page.Load event handler ends. This
process is automatic ...
I wanted to try it on example
create a button declaratively -
<asp:Button ID="Button1" runat="server" Text="Button"
onclick="Button1_Click" />
and dynamically on behind code 5 checkboxes -
protected void Page_Load(object sender, EventArgs e)
{
for (int i = 0; i <= 5; i++)
{
var chBox = new HtmlInputCheckBox();
Controls.Add(chBox);
}
}
But when i check some checkboxes and hit the button, after postback all checkboxes
states are erased. It mean ASP.NET does not manage view states of dynamic controls
automatically? I tried to enable view state to each of checkbox and for whole page,
but its doesn't work.
Can someone explain:
1. Why is it so?
2. How to avoid this?
The reason this is happening is because in order for ASP.NET to restored POSTed values, those controls need to be a part of the page before Load. In order to make this work you need to (if possible) create your controls OnInit of the page.
The controls can be created on Page_Init.
protected void Page_Init(object sender, EventArguments e)
{
//Generate the checkboxes dynamically here.
CheckBox c;
for (int i = 0; i < 5; i++) {
c = new CheckBox();
c.ID = "Checkbox" + i.ToString();
divContainer.Controls.Add(c); //create a div with runat="Server" attribute or use an asp:Panel, etc. container controls.
}
}
After that, try clicking the button again, the state will be always be maintained.
You must set an ID for each dynamic control so that they can be synchronized across postbacks.
As I understand - there is no matter where to create controls in OnInit or OnLoad
(but some books suggests in onLoad), the matter is where to place them - if
you place through Controls.Add - it place them out of <form></form> so postback
does not takes control's states. after cretating a placeholder inside <form></form> and add dynamic controls to this placeholder everthing start to work fine.