Obter texto de caixa de texto criada dinamicamente em asp.net

Eu estive batendo minha cabeça contra isso durante toda a manhã, então espero conseguir alguma ajuda. Essencialmente, estou tendo problemas para obter valores de alguns controles de caixa de texto que estou criando dinamicamente em .net 4.

Aqui está o fluxo desejado do aplicativo.

1). O usuário seleciona um documento html de um menu suspenso que é um modelo para uma carta. Este documento html possui tags do formato $ VARIABLENAME $ que serão substituídas pelos valores corretos.

2). O programa executa o modelo e extrai todas as strings do formato $ STRING $ e as armazena em uma lista.

3). Para cada entrada nesta lista, o programa gera um asp: label e um asp: textbox com um ID exclusivo baseado no campo $ VARIABLENAME $ original.

4). O usuário insere valores de substituição e os resultados são enviados.

5). O programa substitui todos os $ STRING $ pelos valores de substituição e gera o resultado.

Tudo funciona bem até o ponto em que preciso obter valores das caixas de texto. Tenho certeza de que é um problema com o ciclo de vida da página, mas como as caixas de texto não estão sendo geradas até que o uso selecione o modelo desejado na lista suspensa, não sei como fazê-las persistir por meio de postbacks para poder referenciá-las.

Eu estou indo sobre tudo isso errado? Como acesso os campos de texto criados a partir de um evento suspenso depois que um evento postback from submitbutton ocorrer?

EDIT: Aqui está o máximo do 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"]);
        }
    }

Mais tarde, estou tentando adicionar os valores dessas caixas de texto a um dicionário. Parâmetros é o nome do dicionário. O campo chave é o $ STRING $, resultado é o que o usuário inseriu na caixa 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;

Aqui está o 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>

EDIT: Eu coloquei isso em um comentário quando eu descobri isso, mas eu percebi que deveria movê-lo para a pergunta por isso é mais visível:

Pensei que deveria atualizar isso. Eu me sinto um pouco idiota, mas consegui fazer isso funcionar. Basicamente eu estava atribuindo aos controles um ID igual aos tokens de substituição (So ID = "$ FIRSTNAME $13$quot; por exemplo). Nem sequer me ocorreu que os $ 's causariam quaisquer problemas. Quando acabei de mudar para o formato ID = "Firstname" funciona perfeitamente. Obrigado por toda a ajuda!

questionAnswers(3)

yourAnswerToTheQuestion