5

I've been banging my head against this all morning, so hopefully I can get some help. Essentially I'm having issues getting values from some textbox controls I'm creating dynamically in .net 4.

Here's the desired flow of the application.

1). User selects a html document from a dropdown menu that is a template for a letter. This html document has tags of the format $VARIABLENAME$ that will be replaced with the correct values.

2). The program runs though the template and pulls out all strings of the format $STRING$ and stores them in a list.

3). For each entry in this list, the program generates an asp:label and an asp:textbox with a unique ID based on the original $VARIABLENAME$ field.

4). User enters replacement values, and hits submit.

5). Program replaces all $STRING$'s with the replacement values and outputs the result.

Everything works well up to the point where I need to get values from the text boxes. I'm quite sure it's an issue with the page lifecycle, but because the textboxes are not being generated until the use selects the desired template from the dropdown, I'm not sure how to make them persist through postbacks so I can reference them.

Am I going about this all wrong? How do I access the text fields created from a dropdown event after a postback froma submitbutton event occurs?

EDIT: Here's the most of the relevant code.

protected void createTextBoxes(List<string> results)
    {
        if (results != null)
        {
            foreach (string result in results)
            {
                string formattedResult = result.Substring(1, result.Length - 2);
                formattedResult = formattedResult.ToLower();
                formattedResult = char.ToUpper(formattedResult[0]) + formattedResult.Substring(1);


                var label = new Label();
                label.ID = formattedResult;
                label.Text = formattedResult + ": ";
                templateFormPlaceholder.Controls.Add(label);

                var textBox = new TextBox();
                textBox.ID = result;
                templateFormPlaceholder.Controls.Add(textBox);
                templateFormPlaceholder.Controls.Add(new LiteralControl("<br />"));

                previewBtn.Visible = true;
            }
        }
    }

protected void templateDD_SelectedIndexChanged(object sender, EventArgs e)
    {
        var templatePath = "";
        if (templateDD.SelectedIndex == 0)
        {
            previewBtn.Visible = false;
        }

        if (templateDD.SelectedIndex == 1)
        {
            templatePath = employeePath;
        }
        else if (templateDD.SelectedIndex == 2)
        {
            templatePath = managerPath;
        }
        List<string> regMatches = FindMatches(templatePath);
        Session["regMatches"] = regMatches;
        createTextBoxes(regMatches);
    }

protected void Page_Init(object sender, EventArgs e)
    {
        if (Session["regMatches"] != null)
        {
            createTextBoxes((List<string>)Session["regMatches"]);
        }
    }

Later on, I'm trying to add the values from these textboxes to a dictionary. Parameters is the name of the dictionary. The key field is the $STRING$, result is what the user entered in the text box.

   protected void previewBtn_Click(object sender, EventArgs e)
    {
        List<string> matchResults = (List<string>)Session["regMatches"];
        Dictionary<string, string> parameters = new Dictionary<string, string>();
        foreach (string result in matchResults)
        {
            TextBox tb = (TextBox)templateFormPlaceholder.FindControl(result);
            parameters.Add(result, tb.Text);
        }

        var template = ReplaceKeys(parameters);
        outputLBL.Text = template;

Here's the .aspx code.

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="offerLetter.aspx.cs"     Inherits="templateRegexTesting.offerLetter" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
    <p>
        Which template would you like to use?
    </p>
    <asp:DropDownList ID="templateDD" runat="server" OnSelectedIndexChanged="templateDD_SelectedIndexChanged"
        AutoPostBack="true">
        <asp:ListItem></asp:ListItem>
        <asp:ListItem Value="1">Employee</asp:ListItem>
        <asp:ListItem Value="2">Manager</asp:ListItem>
    </asp:DropDownList>
    <br />
    <asp:PlaceHolder ID="templateFormPlaceholder" runat="server" />
    <div>
        <asp:Button ID="previewBtn" runat="server" Text="Preview" Visible="false" OnClick="previewBtn_Click" />
    </div>
    <div>
        <asp:Label ID="outputLBL" runat="server"></asp:Label>
    </div>
    <br />
</div>
</form>
</body>
</html>

EDIT: I put this in a comment when I figured it out, but I figured I should move it into the question so it is more visible:

Thought I should update this. I feel like a bit of an idiot, but I did manage to get this working. Basically I was assigning the controls an ID equal to the replacement tokens (So ID="$FIRSTNAME$" for example). It didn't even dawn on me that the $'s would cause any issues. When I just changed to the format ID="Firstname" it works perfectly. Thank you for all of the help!

mElling
  • 197
  • 1
  • 2
  • 8
  • ..especially where you create the controls. – banging Aug 16 '12 at 17:20
  • [What have you tried?](http://mattgemmell.com/2008/12/08/what-have-you-tried/) – Kash Aug 16 '12 at 17:21
  • Thought I should update this. I feel like a bit of an idiot, but I did manage to get this working. Basically I was assigning the controls an ID equal to the replacement tokens (So ID="$FIRSTNAME$" for example). It didn't even dawn on me that the $'s would cause any issues. When I just changed to the format ID="Firstname" it works perfectly. Thank you for all of the help! – mElling Aug 20 '12 at 14:22

3 Answers3

7

You're right, it's all about the page lifecycle. Dynamically created controls must be re-created at the Page_Init stage, in order to exist before the viewstate binding stage. This means that will have to somehow (using the Session, maybe) store how many textboxes you have created on the previous processing to recreate them. Remind to use the same ID and to add them to your control tree (a repeater or something else that you're using).


UPDATE

Let me give you a suggestion: 1. Declare a class attribute of type List<TextBox> (let's call it CreatedTextBoxes)

  1. Declare a method that receives whatever it needs to create the textboxes. This method must not read anything outside of it's scope. It will simply receive some args, create the textboxes and add them to another control (such as a Repeater). Add each textbox created to CreatedTextBoxes

  2. At the dropdown change event, read the option, save it to the Session and call this method

  3. At Page_Init, verify that object at the Session. If it's null or empty, don't do anything. If it has a value, call that same method, passing the same args

  4. When you need to retrieve that from the dynamically created textboxes, use CreatedTextBoxes and not FindControls()
Andre Calil
  • 7,652
  • 34
  • 41
  • So in the Page_Init stage, do I have to basically run the method where I create the textboxes based on the dropdown selection again? Wouldn't putting this in the Page_Init method cause the page to attempt to create textboxes on the initial visit to the page (before a dropdown selection is made)? – mElling Aug 16 '12 at 17:38
  • Yes and yes, that's why you have to figure out some "flow control". I'll update the question with a suggestion – Andre Calil Aug 16 '12 at 17:45
  • Unfortunately I'm still not able to find the textbox controls to grab the text. I edited my initial post again to include all of the code that seems relevant. – mElling Aug 16 '12 at 18:10
  • @mElling If you debug `previewBtn_Click`, can you retrieve that `TextBox` or is it null? – Andre Calil Aug 16 '12 at 18:25
  • Also, selecting from the dropdown more than once will cause this to break since both the dropdown and the page_init method will attempt to create controls with the same ID. – mElling Aug 16 '12 at 18:26
  • the TextBox tb is null if I set a breakpoint and step through to that point. Also, here is the error that it throws; Exception Details: System.NullReferenceException: Object reference not set to an instance of an object. Source Error: Line 125: { Line 126: TextBox tb = (TextBox)form1.FindControl(result); Line 127: parameters.Add(result, tb.Text); Line 128: } Line 129: – mElling Aug 16 '12 at 18:29
  • @mElling Try starting the create method with `templateFormPlaceholder.Controls.Clear();` – Andre Calil Aug 16 '12 at 18:32
  • That fixed the duplicate control ID issue. I'm still not able to get text though. I verified that the ID I'm trying to find is in fact correct and created on the page. Still nothing. I'm beginning to think I might just move towards a jQuery solution if I can't get this to work soon haha. – mElling Aug 16 '12 at 18:52
  • @mElling Updated the answer. Give a try, please – Andre Calil Aug 16 '12 at 19:06
  • Step in the right direction...it actually finds a text box now. Problem now is that it always grabs just an empty string. I'm assuming now it's a matter of having the values persist through a post back. TextBox tb = createdTextBoxes.Find(text => text.ID == result); parameters.Add(result, tb.Text); tb.Text always = "" now. – mElling Aug 16 '12 at 19:34
  • @mElling When you create the textboxes, enforce the `EnableViewState = true`. Is the viewstate of the page disabled? – Andre Calil Aug 16 '12 at 19:40
  • I added 'textBox.enableViewState = true; to the textBox creation method. Still doesn't pick up values I enter. I haven't touched anything to do with the viewstate until this. – mElling Aug 16 '12 at 19:44
  • @mElling I'm sorry, change that `Page_Init` to `protected internal override void OnInit(EventArgs e)` – Andre Calil Aug 16 '12 at 19:50
  • I changed it to protected override void OnInit(EventArgs e). Using internal spit out this error 'cannot change access modifiers when overriding 'protected' inherited member '. This also did not change anything. – mElling Aug 16 '12 at 19:59
  • @mElling Dude, this is SO weird. Could you post the markup of your placeholder and the page's directives? – Andre Calil Aug 16 '12 at 20:01
  • Added to original post. Just wanted to say thanks for taking all this time...it's been very helpful thus-far. Hopefully we can get this figured out without too much headache! – mElling Aug 16 '12 at 20:06
  • @mElling Nope. Actually, we hadn't solved your problem yet. What code do you run at `Page_Load`? – Andre Calil Aug 16 '12 at 20:21
  • Using separate container for textboxes only hides the issue. You should be able to find control that you just created in the same page life cycle. The fact that textboxes are not found in templateFormPlaceholder is to me the indication of the problem. If the textboxes do not become integral part of the page, they will not get their values from Request. By the way, you can look directly in Request.Form for their values, but you need to know their corresponding ClientID's. – Igor Aug 16 '12 at 20:59
  • @AndreCalil I do not have anything running at Page_Load right now. The only thing on the page initially is the dropdown which has hardcoded values in it for now. – mElling Aug 17 '12 at 11:51
  • @Igor I'm only using the templateFormPlaceholder container for placement reasons. Are you suggesting I should put the textboxes directly onto 'form1' instead of in another container? – mElling Aug 17 '12 at 11:52
  • @mElling No, I was referring to createdTextBoxes list where (as you said above) you were able to find the textboxes. You need to make sure that the textboxes are properly recreated on the postback caused by previewBtn click. Experiment with a different type of container control (e.g. Panel instead of Placeholder), put a design-time textbox inside this Placeholder/Panel and see if you can get it by using FindControl and wheather its text reflects your client-side value. – Igor Aug 17 '12 at 15:13
  • @AndreCalil See my comment above on my original question. Thanks for all of the troubleshooting. It turned out to be something really quite simple. – mElling Aug 20 '12 at 14:23
1

You add TextBox controls to templateFormPlaceholder.Controls but use form1.FindControl to find them. FindControl method will find a control only if the control is directly contained by the specified container - from http://msdn.microsoft.com/en-us/library/486wc64h.aspx. Try calling templateFormPlaceholder.FindControl instead.

Igor
  • 15,833
  • 1
  • 27
  • 32
  • Good catch, but unfortunately changing it to form1 was only a last-ditch shot in the dark attempt. Even changing it back to TextBox tb = (TextBox)templateFormPlaceholder.FindControl(result); parameters.Add(result, tb.Text); results in a nullreferenceexception. I'll update my original post to reflect this. – mElling Aug 16 '12 at 19:01
1

Create Dynamic TextBoxes and add it to a asp panel so that you can access it easily.

Here is the ASP.NET design elements.

<div class="form-group">
<asp:Panel ID="panel" runat="server" CssClass="form-group">

</asp:Panel>
</div>

C# Code to generate Dynamic textboxes

 protected void create_dynamic_text(object sender, EventArgs e)
{
    int num = 5; // you can give the number here
    for (int i = 0; i < num;i++ )
    {
        TextBox tb = new TextBox();
        tb.ID = "txt_box_name" + i.ToString();
        tb.CssClass = "add classes if you need";
        tb.Width = 400; //Manage width and height 
        panel.Controls.Add(tb); //panel is my ASP.Panel object. Look above for the design code of ASP panel
    }
}

C# Code to Take Values

 protected void reade_values(object sender, EventArgs e)
{
   int num=5; // your count goes here
    TextBox tb = new TextBox();
        for (int i = 0; i < num; i++)
        {
           tb=(TextBox)panel.FindControl("txt_box_name"+i.ToString());
           string value = tb.Text; //You have the data now
        }
    }
}
Sinoy Siby
  • 39
  • 4