26

I have a div Conatining a Panel in aspx page

<div id="divNameofParticipants" runat="server">
    <asp:Panel ID="panelNameofParticipants" runat="server">
    </asp:Panel>
</div>

I am populating the panel dynamically from codebehind with the following code:

void btnSubmitCountParticipant_Click(object sender, EventArgs e)
    {
        StringBuilder sbparticipantName=new StringBuilder();

        try
        {
            int numberofparticipants = Convert.ToInt32(drpNoofparticipants.SelectedValue);
            ViewState["numberofparticipants"] = numberofparticipants;
            Table tableparticipantName = new Table();
            int rowcount = 1;
            int columnCount = numberofparticipants;
            for (int i = 0; i < rowcount; i++)
            {
                TableRow row = new TableRow();
                for (int j = 0; j < columnCount; j++)
                {
                    TableCell cell = new TableCell();
                    TextBox txtNameofParticipant = new TextBox();
                    txtNameofParticipant.ID = "txtNameofParticipant" + Convert.ToString(i);
                    cell.ID = "cell" + Convert.ToString(i);
                    cell.Controls.Add(txtNameofParticipant);
                    row.Cells.Add(cell);


                }
                tableparticipantName.Rows.Add(row);
                panelNameofParticipants.Controls.Add(tableparticipantName);

            }

        }
        catch(Exception ex)
        {


        }
    }

Now I want to access the value of these dynamically generated textbox in the codebehind.for which i my code is as under:

public void CreateControls()
    {

        try
        {
            //String test1 = test.Value;
            List<string> listParticipantName = new List<string>();
            if (ViewState["numberofparticipants"] != null)
            {
                int numberofparticipants = Convert.ToInt32(ViewState["numberofparticipants"]);
                for (int i = 0; i < numberofparticipants; i++)
                {
                    string findcontrol = "txtNameofParticipant" + i;
                    TextBox txtParticipantName = (TextBox)panelNameofParticipants.FindControl(findcontrol);
                    listParticipantName.Add(txtParticipantName.Text);

                }
            }
        }
        catch (Exception ex)
        {

        }
    }

but I am not able to get the values in codebehind.

 TextBox txtParticipantName = (TextBox)panelNameofParticipants.FindControl(findcontrol);

the above code is not able to find the control and its always giving null.what am i doing wrong.i recreated the controls in page load also since postback is stateless but still no success.

Thanks in Advance

R R
  • 2,999
  • 2
  • 24
  • 42
  • 7
    the problem on creating dynamic controls in server side is you need to recreate them on every page initialization, if im not wrong, on OnInit method. – Anonymous Duck Jun 16 '15 at 14:57
  • are you open to using something like the gridview? it will save you a load of coding – naveen Jun 16 '15 at 15:08
  • This might help: http://geekswithblogs.net/shahed/archive/2008/06/26/123391.aspx – sr28 Jun 16 '15 at 15:15
  • Most problems with ASP.NET webforms boil down to timing issues. Your code probably runs too early in the page lifecycle. Try to attach to postback event or better to other controls (that are crucial to your page) events, like Click, etc. – Simon Mourier Jun 21 '15 at 14:31
  • ASP.NET has introduced something called ClientID, can you try that? – Akash Kava Jun 23 '15 at 06:43
  • I think your question is same of http://stackoverflow.com/a/4088174/2374987 – Feras Salim Jun 23 '15 at 17:25

11 Answers11

9

You need to create dynamic controls in PreInit not in OnLoad. See documentation here: https://msdn.microsoft.com/en-us/library/ms178472.aspx

Re/Creating the controls on page load will cause the ViewState not to be bound to the controls because viewstate binding happens before OnLoad.

Kent Cooper
  • 4,319
  • 3
  • 19
  • 23
  • Dynamic controls should be created in LoadViewState, not in PreInit, Init, or Load. You do not have access to view state from the previous request in PreInit and Init. – Michael Liu Jun 27 '15 at 18:27
  • No they should be created before that so when load view state occurs the controls can be automatically bound to the view state. See Microsoft's own documentation in the link provided. – Kent Cooper Jun 27 '15 at 19:57
  • The documentation is wrong: the built-in ASP.NET data-bound controls recreate dynamic controls in LoadViewState. (The LoadViewState method of a parent control is called before the view state for child controls is loaded.) – Michael Liu Jun 27 '15 at 20:12
  • The documentation is quite correct. For one the {LoadViewState}[https://msdn.microsoft.com/en-us/library/system.web.ui.control.loadviewstate(v=vs.110).aspx] method is only available from within a Control not a Page. Second the LoadViewState method will only work if the control is part of the Page prior to the viewstate being bound, which is why when you dynamically create a control you should recreate the control in PreInt. – Kent Cooper Jun 27 '15 at 20:22
  • First, a Page *is* a Control and thus has a LoadViewState method. Second, as I noted, the LoadViewState method of a parent control is called before view state for its child controls is loaded, so if you dynamically recreate your child controls in LoadViewState, your child controls' view state will be loaded correctly. Also, note that the OP is tracking the number of dynamic controls in view state. `ViewState["numberofparticipants"]` will be null in PreInit and Init, and will be available in LoadViewState. – Michael Liu Jun 27 '15 at 20:34
6

As mentioned by other people. To create dynamic controls your need to do this for every postback and at the right time. To render dynamic controls, use the Preinit event. I suggest that your have a look at https://msdn.microsoft.com/en-us/library/ms178472(v=vs.80).aspx to learn more about this.

MSDN - PreInit: Use this event for the following: Check the IsPostBack property to determine whether this is the first time the page is being processed. Create or re-create dynamic controls. Set a master page dynamically. Set the Theme property dynamically. Read or set profile property values. NoteNote If the request is a postback, the values of the controls have not yet been restored from view state. If you set a control property at this stage, its value might be overwritten in the next event.

The next interesting event is Preload that states:

MSDN - PreLoad Use this event if you need to perform processing on your page or control before the Load event. After the Page raises this event, it loads view state for itself and all controls, and then processes any postback data included with the Request instance.

Meaning that in the next event Load (Page_Load) the viewstate should be loaded, so here you should effectively be able to check your values.

You also need to make sure that view state is enabled and the easiest is probably in the page level directive:

<%@Page EnableViewState="True" %>

Take a look at the article https://msdn.microsoft.com/en-us/library/ms972976.aspx2 that goes more into the depth of all this

Note

If your problem is that you need to create controls dynamically on a button click, and there will be many controls created, you should probably turn to jQuery ajax and use the attribute [WebMethod] on a public function in your code behind. Creating dynamic controls and maintaining the ViewState is quite costly, so i really recommend this, for a better user experience.

Binke
  • 897
  • 8
  • 25
5

If you use a DataPresentation control like asp:GridView it will be much easier.

Markup

<asp:GridView ID="ParticipantsGrid" runat="server" AutoGenerateColumns="false">   
    <Columns>
        <asp:TemplateField HeaderText="Participants">
            <ItemTemplate>
                <asp:TextBox ID="txtNameofParticipant" runat="server" 
                    Text='<%# Container.DataItem %>'>
                </asp:TextBox>
            </ItemTemplate>
        </asp:TemplateField>
    </Columns>
</asp:GridView>

Code-behind

protected void btnSubmitCountParticipant_Click(object sender, EventArgs e)
{
    try
    {
        var selectedParticipantCount = Convert.ToInt32(drpNoofparticipants.SelectedValue);
        var items = Enumerable.Repeat(string.Empty, selectedParticipantCount).ToList();
        ParticipantsGrid.DataSource = items;
        ParticipantsGrid.DataBind();
    }
    catch (Exception ex)
    { }
}
public void CreateControls()
{

    try
    {
        var participants = ParticipantsGrid.Rows
            .Cast<GridViewRow>()
            .Select(row => ((TextBox)row.FindControl("txtNameofParticipant")).Text)
            .ToList();
    }
    catch (Exception ex)
    { }
}
naveen
  • 53,448
  • 46
  • 161
  • 251
4

I think you are doing that correctly. In asp.net webforms, there are lots of times that you will experienced strange things that are happening. Especially with the presence of ViewState. First we need to troubleshoot our code and try different tests and approaches. That will be my advise for you, pleas use this code and debug if what is really happening:

    void btnSubmitCountParticipant_Click(object sender, EventArgs e)
{
    StringBuilder sbparticipantName=new StringBuilder();

    try
    {
        int numberofparticipants = Convert.ToInt32(drpNoofparticipants.SelectedValue);
        ViewState["numberofparticipants"] = numberofparticipants;
        Table tableparticipantName = new Table();
        int rowcount = 1;
        int columnCount = numberofparticipants;

        TableRow row = new TableRow();
        TableCell cell = new TableCell();
        TextBox txtNameofParticipant = new TextBox();
        txtNameofParticipant.ID = "NameTest";
        txtNameofParticipant.Text = "This is a textbox";
        cell.ID = "cellTest";
        cell.Controls.Add(txtNameofParticipant);
        row.Cells.Add(cell);

        tableparticipantName.Rows.Add(row);
        panelNameofParticipants.Controls.Add(tableparticipantName);

    }
    catch(Exception ex)
    { // please put a breakpoint here, so that we'll know if something occurs, 
    }
}


public void CreateControls()
{
    try
    {
        //String test1 = test.Value;
        List<string> listParticipantName = new List<string>();
        //if (ViewState["numberofparticipants"] != null)
        //{
        string findcontrol = "NameTest";
        TextBox txtParticipantName = (TextBox)panelNameofParticipants.Controls["NameText"];
        //check if get what we want. 
        listParticipantName.Add(txtParticipantName.Text);
        //}
    }
    catch (Exception ex)
    {// please put a breakpoint here, so that we'll know if something occurs, 
    }
}
jomsk1e
  • 3,585
  • 7
  • 34
  • 59
3

Dynamic controls need to be recreated on every post-back. The easiest way to achieve that in your code would be to cache the control structure and repopulate it. This how it can be done:

void btnSubmitCountParticipant_Click(object sender, EventArgs e)
{
    StringBuilder sbparticipantName=new StringBuilder();
    Panel p1 = new Panel();

    try
    {
        int numberofparticipants = Convert.ToInt32(drpNoofparticipants.SelectedValue);
        ViewState["numberofparticipants"] = numberofparticipants;
        Table tableparticipantName = new Table();
        int rowcount = 1;
        int columnCount = numberofparticipants;
        for (int i = 0; i < rowcount; i++)
        {
            TableRow row = new TableRow();
            for (int j = 0; j < columnCount; j++)
            {
                TableCell cell = new TableCell();
                TextBox txtNameofParticipant = new TextBox();
                txtNameofParticipant.ID = "txtNameofParticipant" + Convert.ToString(i);
                cell.ID = "cell" + Convert.ToString(i);
                cell.Controls.Add(txtNameofParticipant);
                row.Cells.Add(cell);


            }
            tableparticipantName.Rows.Add(row);
            p1.Controls.Add(tableparticipantName);
        }
        Cache["TempPanel"] = p1;
        panelNameofParticipants.Controls.Add(p1);

    }
    catch(Exception ex)
    {


    }
}

Look for the p1 panel in the above code to see the changes. Now the code for the CreateControls function need to be changed as following:

public void CreateControls()
{

    try
    {

        Panel p1= (Panel)Cache["TempPanel"];
        panelNameofParticipants.Controls.Add(p1);

        //String test1 = test.Value;
        List<string> listParticipantName = new List<string>();
        if (ViewState["numberofparticipants"] != null)
        {
            int numberofparticipants = Convert.ToInt32(ViewState["numberofparticipants"]);
            for (int i = 0; i < numberofparticipants; i++)
            {
                string findcontrol = "txtNameofParticipant" + i;
                TextBox txtParticipantName = (TextBox)panelNameofParticipants.FindControl(findcontrol);
                listParticipantName.Add(txtParticipantName.Text);

            }
        }
    }
    catch (Exception ex)
    {

    }
}

Hope this helps.

Shashank Chaturvedi
  • 2,756
  • 19
  • 29
1

Dynamically created controls would disappear when page posts back, This article here explains why it happens.

So in order to make dynamic controls be recognized by asp.net page, you need to recreate it in preinit event handler, but I think it is difficult as this dynamic controls in your case rely on some other web form element such as a dropdownlist drpNoofparticipants and also ViewState is not available at this stage.

My suggestion is that we do it in a different way, each post back is actually a form post, so instead of finding the dynamic text box, you could directly get the value via Request.Form collection. Here is the code snippet:

        var list = Request.Form.AllKeys.Where(x => x.Contains("txtNameofParticipant"));
        foreach (var item in list)
        {
            var value = Request.Form[item];
        }

In this way, you could get the value and since you don't need to rely on ASP.NET engine to retrieve the value from dynamically created control, you could postpone the recreating the table in page_load event handler.

Hope it helps.

Kane Wang
  • 106
  • 1
  • 7
1

Put your btnSubmitCountParticipant_Click method data into other function with name of your choice. Call that function in method btnSubmitCountParticipant_Click and in method CreateControls(). Also cut paste the below code in btnSubmitCountParticipant_Click method

int numberofparticipants = Convert.ToInt32(drpNoofparticipants.SelectedValue); ViewState["numberofparticipants"] = numberofparticipants;

. This is working in my machine. Hope this helps

Utkarsh Bhushan
  • 163
  • 2
  • 7
1

Yes,Dynamic controls are lost in postback,So we need save dynamic control values in Viewsate and again generate dynamic control.I added code here, this working fine

//Create Button Dynamic Control
             protected void btnDyCreateControl_Click(object sender, EventArgs e)
                    {

                        try
                        {
                            int numberofparticipants = 5;
                            ViewState["numberofparticipants"] = numberofparticipants;
                            int test = (int)ViewState["numberofparticipants"];
                            int rowcount = 1;
                            int columnCount = numberofparticipants;
                            CreateDynamicTable(rowcount, columnCount);


                        }
                        catch (Exception ex)
                        {


                        }
                    }
                 //submit values
            protected void btnSave_Click(object sender, EventArgs e)
                {
                   try
                {

                    List<string> listParticipantName = new List<string>();
                    if (ViewState["numberofparticipants"] != null)
                    {
                        int numberofparticipants = Convert.ToInt32(ViewState["numberofparticipants"]);

                            foreach (Control c in panelNameofParticipants.Controls)
                            {   
                               if (c is Table)
                                {
                                    foreach (TableRow row in c.Controls)
                                    {
                                        int i = 0;
                                        foreach (TableCell cell in row.Controls)
                                        {

                                            if (cell.Controls[0] is TextBox)
                                            {
                                                string findcontrol = "txtNameofParticipant" + i;
                                                TextBox txtParticipantName = (TextBox)cell.Controls[0].FindControl(findcontrol);
                                                listParticipantName.Add(txtParticipantName.Text);

                                            }
                                            i++;

                                        }


                                    }
                                }
                            }

                    }
                }
                catch (Exception ex)
                {

                }
                }

              //Save ViewState
        protected override object SaveViewState()
                {
                    object[] newViewState = new object[3];

                    List<string> txtValues = new List<string>();
                    foreach (Control c in panelNameofParticipants.Controls)
                    {
                        if (c is Table)
                        {
                            foreach (TableRow row in c.Controls)
                            {
                                foreach (TableCell cell in row.Controls)
                                {
                                    if (cell.Controls[0] is TextBox)
                                    {
                                        txtValues.Add(((TextBox)cell.Controls[0]).Text);
                                    }
                                }
                            }
                        }
                    }

                    newViewState[0] = txtValues.ToArray();
                    newViewState[1] = base.SaveViewState();
                    if (ViewState["numberofparticipants"] != null)
                        newViewState[2] = (int)ViewState["numberofparticipants"];
                    else
                        newViewState[2] = 0;
                    return newViewState;
                }
    //Load ViewState

    protected override void LoadViewState(object savedState)
            {
                //if we can identify the custom view state as defined in the override for SaveViewState
                if (savedState is object[] && ((object[])savedState).Length == 3 && ((object[])savedState)[0] is string[])
                {
                    object[] newViewState = (object[])savedState;
                    string[] txtValues = (string[])(newViewState[0]);
                    if (txtValues.Length > 0)
                    {
                        //re-load tables
                        CreateDynamicTable(1, Convert.ToInt32(newViewState[2]));
                        int i = 0;
                        foreach (Control c in panelNameofParticipants.Controls)
                        {
                            if (c is Table)
                            {
                                foreach (TableRow row in c.Controls)
                                {
                                    foreach (TableCell cell in row.Controls)
                                    {
                                        if (cell.Controls[0] is TextBox && i < txtValues.Length)
                                        {
                                            ((TextBox)cell.Controls[0]).Text = txtValues[i++].ToString();

                                        }
                                    }
                                }
                            }
                    }
                    }
                    //load the ViewState normally
                    base.LoadViewState(newViewState[1]);
                }
                else
                {
                    base.LoadViewState(savedState);

                }
            }
//Create Dynamic Control

public void CreateDynamicTable(int rowcount, int columnCount)
        {
            Table tableparticipantName = new Table();

             for (int i = 0; i < rowcount; i++)
                {
                    TableRow row = new TableRow();
                    for (int j = 0; j < columnCount; j++)
                    {
                        TableCell cell = new TableCell();

                        TextBox txtNameofParticipant = new TextBox();
                        txtNameofParticipant.ID = "txtNameofParticipant" + Convert.ToString(j);
                        cell.ID = "cell" + Convert.ToString(j);
                        cell.Controls.Add(txtNameofParticipant);
                        row.Cells.Add(cell);
                    }
                    tableparticipantName.Rows.Add(row);
                    tableparticipantName.EnableViewState = true;
                    ViewState["tableparticipantName"] = true;
        }
             panelNameofParticipants.Controls.Add(tableparticipantName);
        }

Reference Link,MSDN Link, Hope it helps.

Benjamin
  • 91
  • 5
1

When creating your textboxes make sure you set the ClientIDMode I recently worked on something similar. I dynamically created checkboxes in GridView rows and it worked for me.

TextBox txtNameofParticipant = new TextBox();
txtNameofParticipant.ID = "txtNameofParticipant" + Convert.ToString(i);
txtNameOfParticipant.ClientIDMode = System.Web.UI.ClientIDMode.Static;
1

Dynamically added controls have to be created again on postback, otherwise the state of the dynamically added controls will get lost (if the controls lost how can one preserve their old state) . Now the question. When is the viewstated loaded? It is loaded in between the two events Page_Init and Load.

Following code samples are a bit modification of your code for your understanding

The aspx markup is same as yours, but with some extra controls

<form id="form1" runat="server">
    <asp:DropDownList ID="drpNoofparticipants" runat="server"  >
        <asp:ListItem>1</asp:ListItem>
        <asp:ListItem>2</asp:ListItem>
        <asp:ListItem>3</asp:ListItem>
        <asp:ListItem>4</asp:ListItem>
        <asp:ListItem>5</asp:ListItem>
        <asp:ListItem>6</asp:ListItem>
        <asp:ListItem>7</asp:ListItem>
        <asp:ListItem>8</asp:ListItem>
        <asp:ListItem>9</asp:ListItem>
        <asp:ListItem>10</asp:ListItem>
    </asp:DropDownList>
    <br /><asp:Button ID="btnCreateTextBoxes" runat="server" OnClick="btnSubmitCountParticipant_Click" Text="Create TextBoxes" />
    <div id="divNameofParticipants" runat="server">
        <asp:Panel ID="panelNameofParticipants" runat="server">
        </asp:Panel>
    </div>
    <div>
        <asp:Button ID="btnSubmitParticipants" runat="server" Text="Submit the Participants" OnClick="BtnSubmitParticipantsClicked" />
    </div>
</form>

On click event of the btnCreateTextBoxes button i am creating the controls using the following code

private void CreateTheControlsAgain(int numberofparticipants)
    {


        try
        {
            ViewState["numberofparticipants"] = Convert.ToString(numberofparticipants);
                Table tableparticipantName = new Table();
                int rowcount = 1;
                int columnCount = numberofparticipants;
                for (int i = 0; i < rowcount; i++)
                {

                    for (int j = 0; j < columnCount; j++)
                    {
                        TableRow row = new TableRow();
                        TableCell cell = new TableCell();
                        TextBox txtNameofParticipant = new TextBox();
                        txtNameofParticipant.ID = "txtNameofParticipant" + Convert.ToString(j);
                        cell.ID = "cell" + Convert.ToString(j);
                        cell.Controls.Add(txtNameofParticipant);
                        row.Cells.Add(cell);
                        tableparticipantName.Rows.Add(row);

                    }



                }
                panelNameofParticipants.Controls.Add(tableparticipantName);
            }



        catch (Exception ex)
        {


        }
    }

As mentioned above to maintain the control state, i included the re-creation of the controls in the page Load event based on the viewstate["numberofparticipants"]

 protected void Page_Load(object sender, EventArgs e)
    {
        if (ViewState["numberofparticipants"] != null)
        {
            CreateTheControlsAgain(Convert.ToInt32(ViewState["numberofparticipants"]));
            CreateControls();

        }

    }

On click event of btnSubmitParticipants button i wrote the following event and writing the participants names to the console

try
        {
            //String test1 = test.Value;
            List<string> listParticipantName = new List<string>();
            if (ViewState["numberofparticipants"] != null)
            {
                int numberofparticipants = Convert.ToInt32(ViewState["numberofparticipants"]);
                for (int i = 0; i < numberofparticipants; i++)
                {
                    string findcontrol = "txtNameofParticipant" + i;
                    var txtParticipantName = panelNameofParticipants.FindControl(string.Format("txtNameofParticipant{0}", i)) as TextBox;
                    listParticipantName.Add(txtParticipantName.Text);

                }
            }
            foreach (var item in listParticipantName)
            {
                Response.Write(string.Format("{0}<br/>", item));
            }
        }
        catch (Exception ex)
        {

        }

Hope this helps

Tanzeel ur Rehman
  • 429
  • 2
  • 6
  • 18
-1

I'm not sure if this is desired, but you're adding multiple tables to your panel, but I think you only want/need one table. So this line should be outside the outer for loop like so:

for (int i = 0; i < rowcount; i++)
{
     TableRow row = new TableRow();
     for (int j = 0; j < columnCount; j++)
     {
          TableCell cell = new TableCell();
          TextBox txtNameofParticipant = new TextBox();
          txtNameofParticipant.ID = "txtNameofParticipant" + Convert.ToString(i);
          cell.ID = "cell" + Convert.ToString(i);
          cell.Controls.Add(txtNameofParticipant);
          row.Cells.Add(cell);
     }
     tableparticipantName.Rows.Add(row);
}

panelNameofParticipants.Controls.Add(tableparticipantName);

Additionally, FindControl will only search the direct children of the container, in this case it can only find the table, else it'll return null. so you need to search children of the panel to find the control, e.g. your table:

foreach (TableRow row in tableparticipantName.Rows)
{
    TextBox txtParticipantName = (TextBox)row.FindControl(findcontrol);
    listParticipantName.Add(txtParticipantName.Text);
}

If that doesn't work then doing it recursively might work better: ASP.NET Is there a better way to find controls that are within other controls?

Community
  • 1
  • 1
AzNjoE
  • 733
  • 3
  • 9
  • 18