Obtenga texto de un cuadro de texto creado dinámicamente en asp.net

Me he estado golpeando la cabeza contra esto toda la mañana, así que espero poder obtener ayuda. Esencialmente tengo problemas para obtener valores de algunos controles de cuadro de texto que estoy creando dinámicamente en .net 4.

Aquí está el flujo deseado de la aplicación.

1). El usuario selecciona un documento html de un menú desplegable que es una plantilla para una letra. Este documento html tiene etiquetas con el formato $ VARIABLENAME $ que se reemplazarán con los valores correctos.

2). El programa se ejecuta a través de la plantilla y extrae todas las cadenas del formato $ STRING $ y las almacena en una lista.

3). Para cada entrada de esta lista, el programa genera una etiqueta asp: label y un cuadro de texto asp: con una ID única basada en el campo $ VARIABLENAME $ original.

4). El usuario introduce valores de reemplazo y pulsa enviar.

5). El programa reemplaza todos los $ STRING $ con los valores de reemplazo y genera el resultado.

Todo funciona bien hasta el punto en que necesito obtener valores de los cuadros de texto. Estoy bastante seguro de que es un problema con el ciclo de vida de la página, pero como los cuadros de texto no se generan hasta que el uso selecciona la plantilla deseada del menú desplegable, no estoy seguro de cómo hacer que persistan a través de las devoluciones de datos para poder consultarlos.

¿Estoy yendo todo esto mal? ¿Cómo accedo a los campos de texto creados a partir de un evento desplegable después de que se produzca una devolución de un evento submitbutton?

EDITAR: Aquí está la mayor parte del código relevante.

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"]);
        }
    }

Más adelante, estoy tratando de agregar los valores de estos cuadros de texto a un diccionario. Parámetros es el nombre del diccionario. El campo clave es $ STRING $, el resultado es lo que el usuario ingresó en el cuadro de texto.

   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;

Aquí está el código .aspx.

<%@ 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>

EDITAR: Puse esto en un comentario cuando lo descubrí, pero pensé que debería pasarlo a la pregunta para que sea más visible:

Pensé que debería actualizar esto. Me siento un poco idiota, pero logré que esto funcionara. Básicamente, estaba asignando a los controles una ID igual a los tokens de reemplazo (por lo tanto, ID = "$ FIRSTNAME $13$quot; por ejemplo). Ni siquiera me di cuenta de que los $ 's podrían causar problemas. Cuando acabo de cambiar al formato ID = "Nombre" funciona perfectamente. ¡Gracias a todos por la ayuda!

Respuestas a la pregunta(3)

Su respuesta a la pregunta