15

I know this question has been asked thousands of times, and I've struggled with it before, but for some reason, I can't accomplish what I want to accomplish... I have a dynamically added LinkButton that when clicked will dynamically add a control (in this example, a textbox) to the same panel. The intent is to continuously add on as many controls as times the LinkButton was clicked (i.e. I click it once, one box, then another click will give me 2 boxes, another click adds a 3rd). In the code below, I use the current date and time serialized to create a unique ID for each textbox control.

When I execute the code, clicking "Add Filter" will generate a new textbox, but once clicked again will create a new one, and dispose of the one before it. Instead, I want to persist the previous textbox as well as any data submitted within it.

Your help is appreciated.

In the aspx:

<asp:Panel ID="pnlFilter" runat="server">

</asp:Panel>

In the aspx.cs:

protected void Page_Init(object sender, EventArgs e)
{
        LinkButton lb = new LinkButton();
        lb.ID = "lbAddFilter";
        pnlFilter.Controls.Add(lb);
        lb.Text = "Add Filter";
        lb.Click += new EventHandler(lbAddFilter_Click);
}


void lbAddFilter_Click(object sender, EventArgs e)
{
    TextBox tb = new TextBox();
    tb.ID = "tb" + DateTime.Now.ToBinary().ToString();
    pnlFilter.Controls.Add(tb);
}
Honus Wagner
  • 2,680
  • 11
  • 41
  • 61

4 Answers4

16

For anyone else trying to do something like this: don't. Instead, think of the flow of information and understand that there is a better way to do it. The input control(s) do not need to be dynamically created. They can be static, and upon filling out and submitting on one, that information has to go somewhere (database, cache, session, for example). Once its there, on postback, loop through all items in your storage of choice and create a display for them.

That is what I've done and it has made life a lot easier. Hope it helps someone.

Honus Wagner
  • 2,680
  • 11
  • 41
  • 61
  • What would you do in a situation where questionaires (polls) are created in a certain application, and presented trough a webservice the website fetches a poll and renders the fields for it? in that case dynamically creating content is the only suitable option no? – Sander May 31 '11 at 09:14
  • Yes, dynamically creating the content is in order, but it does not have to be incremental like I was trying to do; I assume your web service returns all results each time, in which case you rended all of them every time. With use of viewstate and caching, your performance should be pretty good. – Honus Wagner May 31 '11 at 16:23
  • 1
    You're a genius. I was trying to achieve same thing as the OP but this made me realise I can instead of adding multiple control instances, I can just show the new control in a modal window and on "save" in her modal, commit to the data store and then load that onto the page very easily. A thousand thankyou's. I've spent 3 days and all of tonight trying to figure this one out. – Samuel MacLachlan Jul 23 '14 at 14:30
  • What about dynamically pagination link, not all pages are created. Creating those page links are required so postback event can be caught. If not, you don't know which page link is being clicked. – iroel Jun 25 '20 at 03:33
11
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:

  1. Page calls OnInit and adds your LinkButton
  2. The user clicks the LinkButton
  3. Page calls OnInit to begin the postback request. The LinkButton is recreated
  4. The event handler is called, which dynamically creates your TextBox and adds it to the control collection
  5. The user clicks the LinkButton again
  6. 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.
  7. 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.

Stefan Mohr
  • 2,128
  • 2
  • 24
  • 35
1

I believe that you'll need to recreate each control on every postback.

I know that the Repeater control stores enough information about its children to recreate them when databound... You may be able to use it to save a little work.

AaronSieb
  • 7,706
  • 8
  • 37
  • 56
  • You need to recreate them on every postback is correct. Somewhere you need to store how many you already have. Maybe the in the Viewstate or something. – Remy Nov 18 '10 at 19:21
0

Try adding,

if(!Page.IsPostback)
  --- your code that adds different controls.
Kevin Panko
  • 7,844
  • 19
  • 46
  • 58
Rahul Soni
  • 4,699
  • 2
  • 28
  • 55
  • 1
    I wrapped everything inside Page_Init in the !IsPostBack if. Doing that loaded the linkbuttons and the initial state of the page, but when I click on one of those linkbuttons, all of them disappear and nothing is on the page. – Honus Wagner Nov 18 '10 at 15:36