void lbAddFilter_Click(object sender, EventArgs e)
{
TextBox tb = new TextBox();
tb.ID = "tb" + DateTime.Now.ToBinary().ToString();
pnlFilter.Controls.Add(tb);
}
That's not going to work - it's a lifecycle issue as you inferred in your question. There are lots of ways you can work with this, but Honus Wagner's suggestion is the most flexible.
What's happening here is that you're calling Controls.Add in the event handler and the control is being added to the control list, and on the next request (be it a partial postback, full postback, or new page hit), that control is gone because you haven't persisted it anywhere.
Here's what's happening in your page event lifecycle right now:
- Page calls OnInit and adds your LinkButton
- The user clicks the LinkButton
- Page calls OnInit to begin the postback request. The LinkButton is recreated
- The event handler is called, which dynamically creates your TextBox and adds it to the control collection
- The user clicks the LinkButton again
- Page calls OnInit to add the LinkButton again. The pnlFilter.Controls collection is empty at this point, except for any elements you've declared statically in your markup.
- Event handler is called and a new TextBox is added to the control list.
I'm not sure that it's the best practice, but I've had success with a model similar to this (note that I haven't tested this code - it may need adjustment):
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
LinkButton lb = new LinkButton();
lb.ID = "lbAddFilter";
pnlFilter.Controls.Add(lb);
lb.Text = "Add Filter";
lb.Click += new EventHandler(lbAddFilter_Click);
// regenerate dynamically created controls
foreach ( var control in PersistedControls )
{
pnlFilter.Controls.Add(GenerateControl(control));
}
}
private IList<Control> _persistedControls;
private const string PersistedControlsSessionKey = "thispage_persistedcontrols";
private IList<Control> PersistedControls
{
if (_persistedControls == null)
{
if (Session[PersistedControlsSessionKey] == null)
Session[PersistedControlsSessionKey] = new List<Control>();
_persistedControls = Session[PersistedControlsSessionKey] as IList<Control>;
}
return _persistedControls;
}
void lbAddFilter_Click(object sender, EventArgs e)
{
TextBox tb = new TextBox();
tb.ID = "tb" + DateTime.Now.ToBinary().ToString();
PersistedControls.Add(tb);
pnlFilter.Controls.Add(tb);
}
Note that I'm not sure about adding actual Control objects to the Session store - I believe there are issues with the control IDs that will cause errors on postback (maybe a Control isn't serializable?). In the solution I developed, I generated a fresh TextBox/Label/ComboBox/etc in my equivalent of the GenerateControl method, and stored a custom container class in the PersistedControls collection that contained the various properties of the UI control that I would need to generate it on each page Init.