Dynamic control creation - asp.net

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.

Related

Dynamic controls: associated events don't fire if recreated during Page_Load

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;
}

Create Dynamic Controls in PreInit Event of Page Life Cycle?

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?

How to dynamically add user controls within a user control

I want to create a user control that will again contain a user control that will be dynamically generated and can be repeated N times with respect to data present in data table.
How can i do this.
You want to use TemplateControl.LoadControl (which both Page, UserControl and MasterPage inherit from). You need to do this during the Init event for postback stuff to work.
void Page_Init(Object Sender, EventArgs e) {
var control = LoadControl("Awesome.ascx");
// ... Magic!
}
Place a placeholder control on the parent user control:
<asp:PlaceHolder runat="server" id="phMainHolder" />
In the Page_Load or Page_Init event you simply instantiate the child user controls in a for(each) loop and add them to the placeholder like for example:
for(int i = 0; i < 10; i++) {
ChildUC child = new ChildIC();
phMainHolder.Controls.Add(child);
}
This is the bare minimum to do it. You can opt to set properties or whatever on your child controls of course.
The class ChildUC is a fictive class name for this example. Use the class, and if needed use the namespace as extra in which it is defined, of the user control you want to use.

ASP.NET: Postback processed without events being fired

I have a GridView with dynamically created image buttons that should fire command events when clicked. The event handling basically works, except for the very first time a button is clicked. Then, the postback is processed, but the event is not fired.
I have tried to debug this, and it seems to me, that the code executed before and after the first click is exactly the same as for any other clicks. (With the exception that in the first click, the event handler is not called.)
There is some peculiarity in that: The buttons which fire the event are created dynamically through databinding, i.e. databinding must be carried out twice in the page lifecycle: Once on load, in order to make the buttons exist (otherwise, events could not be handled at all), and once before rendering in order to display the new data after the events have been processed.
I have read these posts but they wouldn't match my situation:
ASP.NET LinkButton OnClick Event Is Not Working On Home Page,
LinkButton not firing on production server,
ASP.NET Click() event doesn't fire on second postback
To the details:
The GridView contains image buttons in each row. The images of the buttons are databound. The rows are generated by GridView.DataBind(). To achieve this, I have used the TemplateField with a custom ItemTemplate implementation. The ItemTemplate's InstantiateIn method creates the ImageButton and assigns it the according event handler. Further, the image's DataBinding event is assigned a handler that retrieves the appropriate image based on the respective row's data.
The GridView is placed on a UserControl. The UserControl defines the event handlers for the GridView's events. The code roughly looks as follows:
private DataTable dataTable = new DataTable();
protected SPGridView grid;
protected override void OnLoad(EventArgs e)
{
DoDataBind(); // Creates the grid. This is essential in order for postback events to work.
}
protected override void Render(HtmlTextWriter writer)
{
DoDataBind();
base.Render(writer); // Renews the grid according to the latest changes
}
void ReadButton_Command(object sender, CommandEventArgs e)
{
ImageButton button = (ImageButton)sender;
GridViewRow viewRow = (GridViewRow)button.NamingContainer;
int rowIndex = viewRow.RowIndex;
// rowIndex is used to identify the row in which the button was clicked,
// since the control.ID is equal for all rows.
// [... some code to process the event ...]
}
private void DoDataBind()
{
// [... Some code to fill the dataTable ...]
grid.AutoGenerateColumns = false;
grid.Columns.Clear();
TemplateField templateField = new TemplateField();
templateField.HeaderText = "";
templateField.ItemTemplate = new MyItemTemplate(new CommandEventHandler(ReadButton_Command));
grid.Columns.Add(templateField);
grid.DataSource = this.dataTable.DefaultView;
grid.DataBind();
}
private class MyItemTemplate : ITemplate
{
private CommandEventHandler commandEventHandler;
public MyItemTemplate(CommandEventHandler commandEventHandler)
{
this.commandEventHandler = commandEventHandler;
}
public void InstantiateIn(Control container)
{
ImageButton imageButton = new ImageButton();
imageButton.ID = "btnRead";
imageButton.Command += commandEventHandler;
imageButton.DataBinding += new EventHandler(imageButton_DataBinding);
container.Controls.Add(imageButton);
}
void imageButton_DataBinding(object sender, EventArgs e)
{
// Code to get image URL
}
}
Just to repeat: At each lifecycle, first the OnLoad is executed, which generates the Grid with the ImageButtons. Then, the events are processed. Since the buttons are there, the events usually work. Afterwards, Render is called, which generates the Grid from scratch based upon the new data. This always works, except for the very first time the user clicks on an image button, although I have asserted that the grid and image buttons are also generated when the page is sent to the user for the first time.
Hope that someone can help me understand this or tell me a better solution for my situation.
A couple problems here. Number one, there is no IsPostBack check, which means you're databinding on every load... this is bound to cause some problems, including events not firing. Second, you are calling DoDataBind() twice on every load because you're calling it in OnLoad and Render. Why?
Bind the data ONCE... and then again in reaction to events (if needed).
Other issue... don't bind events to ImageButton in the template fields. This is generally not going to work. Use the ItemCommand event and CommandName/CommandArgument values.
Finally... one last question for you... have you done a comparison (windiff or other tool) on the HTML rendered by the entire page on the first load, and then subsequent loads? Are they EXACTLY the same? Or is there a slight difference... in a control name or PostBack reference?
Well I think the event dispatching happens after page load. In this case, its going to try to run against the controls created by your first data-binding attempt. This controls will have different IDs than when they are recreated later. I'd guess ASP.NET is trying to map the incoming events to a control, not finding a control, and then thats it.
I recommend taking captures of what is in the actual post.
ASP.NET is pretty crummy when it comes to event binding and dynamically created controls. Have fun.
Since in my opinion this is a partial answer, I re-post it this way:
If I use normal Buttons instead of ImageButtons (in the exact same place, i.e. still using MyItemTemplate but instantiating Button instead of ImageButton in "InstantiateIn", it works fine.
If I assert that DoDataBind() is always executed twice before sending the content to the client, it works fine with ImageButtons.
Still puzzled, but whatever...

ASP.NET [Need an event after page load (After GridView renew data) ]

I renew parameters of my DataSource for GridView on Button and loads event Page_Load()
and After it (or with it) GridView renews itself.
I need to rename Header rows but there is no event after Page Load.
more information about details here :
[On Button Click] I change DataSource and Bind it (SqlDataSource1.DataBind(); ) Then sure Page gone to Refresh elements.
[On Page_Load] GridView is changing data, but if I can't change Headers there because it's looking like loads after this function :(
[one more Button] I can add new button with a function - rename headers and it works correct everytime
protected void Button2_Click(object sender, EventArgs e)
{
// Stack overflow
DataRow T;
if (go)
if (GridView2.HeaderRow.Cells.Count != 0)
for (int i = 0; i < SQF.SQF.Permission.Rows.Count; i++)
{
T = SQF.SQF.Permission.Rows[i];
GridView2.HeaderRow.Cells[i].Text = (string)T["Caption1"];
WebMsgBox.Show((string)T["Caption1"]);
}
}
Trouble : I no need one more button for it, I need to rename header rows after I click button to change Data but I have no idea where I can do it.
thank you.
Well you could use Gridview DataBound event, like that:
<asp:GridView runat="server" ID="SomeID" OnDataBound="GridVIew_OnDataBound" ... />
The DataBound event occurs right after the databinding process for that particular control has completed.
After Page_Load there is an event Page.PreRender.
Maybe it will suit you.
EDIT.
All events of your components, such as GridView, are fired between Page.Load and Page.PreRender. Consider using RowDataBound and RowUpdated events.

Resources