user641687
user641687

Reputation:

Can't get values out of dynamically added textbox

Try as I might I can't get the values out of my dynamically added textboxes. I've tried enabling the ViewState in all the containing controls as well as adding the textbox values to the ViewState in the Page_Init event, but the Text property always comes up empty. Here's my code behind page...

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using O2S.Components.PDF4NET;
using O2S.Components.PDF4NET.Graphics;
using O2S.Components.PDF4NET.Graphics.Fonts;
using O2S.Components.PDF4NET.Graphics.Shadings;
using O2S.Components.PDF4NET.Graphics.Shapes;

namespace PDFFormToGrid
{
    public partial class Form : System.Web.UI.Page
    {
        private static double leftMargin = 15;
        private static double rightMargin = 15;
        private static double topMargin = 20;
        private static double minBottomMargin = 20;
        private static double rowHeight = 30;
        private static double headerHeight = 30;
        private static double firstPageTopOffset = 50;
        private static double[] columnsWidth = { 120, 120, 120, 120 };
        private static string[] columnsHeader = { "Name", "Phone Number", "Address", "Company" };
        private static List<Control> panels = new List<Control>();
        private static List<TextBox> fields = new List<TextBox>();

        protected void Page_Load(object sender, EventArgs e)
        {
            if (!Page.IsPostBack)
            {
                panels.Clear();
            }
        }

        protected void btnSubmit_Click(object sender, EventArgs e)
        {
            PDFDocument form = CreatePDF(this);

            Response.ContentType = "application/x-msdownload";
            Response.AddHeader("content-disposition", "attachment;filename=form.pdf");
            form.Save(Response.OutputStream);
            Response.Flush();
            Response.End();
        }

        private static PDFDocument CreatePDF(Form form)
        {
            // Create the PDF document
            PDFDocument doc = new PDFDocument();

            // Create the graphics objects
            PDFFont titleFont = new PDFFont(PDFFontFace.Helvetica, 35);
            PDFBrush purpleTitleBrush = new PDFBrush(new PDFRgbColor(19, 19,105));

            formatDoc(doc);

            // Create first page of report
            PDFPage page = doc.AddPage();

            formatPage(page, titleFont, purpleTitleBrush);

            DrawReportRows(doc, page, panels);

            return doc;
        }


        private static void formatDoc(PDFDocument doc)
        {
            doc.SerialNumber = "Serial Number Goes Here";
            doc.PageOrientation = PDFPageOrientation.Landscape;
        }

        private static void formatPage(PDFPage page, PDFFont font, PDFBrush brush)
        {
            // Draw title of report
            page.Canvas.DrawText("Applicant", font, null, brush, page.Width / 2, 
                15, 0, PDFTextAlign.TopCenter);

            // Draw report header
            DrawReportHeader(page, firstPageTopOffset);
        }

        private static void DrawReportHeader(PDFPage page, double headerTopOffset)
        {
            // Create the graphics objects
            PDFFont headerFont = new PDFFont(PDFFontFace.Helvetica, 12);
            headerFont.Bold = true;
            PDFPen borderColor = new PDFPen(new PDFRgbColor(), 4);
            PDFBrush textColor = new PDFBrush(new PDFRgbColor());
            PDFBrush headerFill = new PDFBrush(new PDFRgbColor(192, 192, 193));

            // Draw the header
            page.Canvas.DrawRectangle(borderColor, headerFill, leftMargin, headerTopOffset,
                page.Width - leftMargin - rightMargin, headerHeight);

            double xOffset = leftMargin;
            // Draw the column separators
            for (int i = 0; i < columnsWidth.Length - 1; i++)
            {
                xOffset = xOffset + columnsWidth[i];
                page.Canvas.DrawLine(borderColor, xOffset, headerTopOffset,
                    xOffset, headerTopOffset + headerHeight);
            }

            xOffset = leftMargin;
            // Draw the columns header
            for (int i = 0; i < columnsWidth.Length; i++)
            {
                page.Canvas.DrawText(columnsHeader[i], headerFont, null, textColor,
                    xOffset + columnsWidth[i] / 2, headerTopOffset + headerHeight / 2, 0, PDFTextAlign.MiddleCenter);
                xOffset = xOffset + columnsWidth[i];
                xOffset = xOffset + columnsWidth[i];
            }

        }

        private static void DrawReportRows(PDFDocument doc, PDFPage page, List<Control> panels)
        {
            // Get number of forms - there's one more than count because of the initial form
            int numForms = panels.Count;

            double rowOffset = firstPageTopOffset + headerHeight;
            for (int i = 0; i < numForms; i++)
            {
                if (rowOffset + rowHeight > page.Height - minBottomMargin)
                { // not enough space to draw a new row, then create a new page, 
                    // draw the header, then draw the rows.
                    page = doc.AddPage();
                    DrawReportHeader(page, topMargin);
                    rowOffset = topMargin + headerHeight;
                }
                DrawReportRow(page, rowOffset, panels[i]);
                rowOffset = rowOffset + rowHeight;
            }
        }

        private static void DrawReportRow(PDFPage page, double topOffset, Control panel)
        {
            // Create the graphic objects
            PDFFont fontText = new PDFFont(PDFFontFace.Helvetica, 12);
            PDFPen borderColor = new PDFPen(new PDFRgbColor(), 1);
            PDFBrush textColor = new PDFBrush(new PDFRgbColor());
            PDFBrush headerFill = new PDFBrush(new PDFRgbColor(192, 192, 192));

            // Draw rows borders
            PDFPoint[] points = { new PDFPoint( leftMargin, topOffset ), 
                                  new PDFPoint( leftMargin, topOffset + rowHeight), 
                                  new PDFPoint( page.Width - rightMargin, topOffset + rowHeight ), 
                                  new PDFPoint( page.Width - rightMargin, topOffset )};
            page.Canvas.DrawLines(borderColor, points);

            double xOffset = leftMargin;
            // Draw the columns separators
            for (int i = 0; i < columnsWidth.Length - 1; i++)
            {
                xOffset = xOffset + columnsWidth[i];
                page.Canvas.DrawLine(borderColor, xOffset, topOffset,
                    xOffset, topOffset + rowHeight);
            }

            // Get all form fields from panel
            fields.Clear();
            GetFormFields(panel);

            xOffset = leftMargin;
            // Draw the columns header
            for (int i = 0; i < columnsWidth.Length; i++)
            {
                // Get ith field text 
                string fieldText = fields[i].Text;
                page.Canvas.DrawText(fieldText, fontText, null, textColor,
                    xOffset + 2, topOffset + rowHeight / 2, 0, PDFTextAlign.MiddleLeft);
                xOffset = xOffset + columnsWidth[i];
            }
        }

        private static void GetFormFields(Control panelControl)
        {
            ControlCollection controls = panelControl.Controls;
            foreach (Control childControl in panelControl.Controls)
            {
                if (childControl.GetType().ToString() == "System.Web.UI.WebControls.TextBox")
                {
                    TextBox txt = childControl as TextBox;
                    fields.Add(txt);
                }
                else
                {
                    GetFormFields(childControl);
                }
            }
        }

        protected void btnAddForm_Click(object sender, EventArgs e)
        {
            // Create Labels
            Label lblName = new Label();
            lblName.Text = "NAME:";
            Label lblNumber = new Label();
            lblNumber.Text = "NUMBER:";
            Label lblAddress = new Label();
            lblAddress.Text = "ADDRESS:";
            Label lblCompany = new Label();
            lblCompany.Text = "COMPANY:";

            // Create Text Boxes
            TextBox txtName = new TextBox();
            TextBox txtNumber = new TextBox();
            TextBox txtAddress = new TextBox();
            TextBox txtCompany = new TextBox();

            // Create submit button
            Button btnSubmit = new Button();
            btnSubmit.Text = "SUBMIT";

            // Create panel and add controls
            Panel pnlForm = new Panel();
            pnlForm.EnableViewState = true;
            pnlForm.Controls.Add(lblName);
            pnlForm.Controls.Add(txtName);
            pnlForm.Controls.Add(new LiteralControl("<br /><br />"));
            pnlForm.Controls.Add(lblNumber);
            pnlForm.Controls.Add(txtNumber);
            pnlForm.Controls.Add(new LiteralControl("<br /><br />"));
            pnlForm.Controls.Add(lblAddress);
            pnlForm.Controls.Add(txtAddress);
            pnlForm.Controls.Add(new LiteralControl("<br /><br />"));
            pnlForm.Controls.Add(lblCompany);
            pnlForm.Controls.Add(txtCompany);
            pnlForm.Controls.Add(new LiteralControl("<hr />"));
            pnlForm.Controls.Add(new LiteralControl("<br /><br />"));
            panels.Add(pnlForm);

            foreach (Control panel in panels)
            {
                phFormContent.Controls.Add(panel);
            }

        }

TextBoxes are added in the btnAddForm_Click() function, and inside of DrawReportRow() (which is itself triggered by another button click event) GetFormFields() is called, which adds all the dynamically added TextBoxes to a static List. Here's the .aspx file....

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Form.aspx.cs" Inherits="PDFFormToGrid.Form" EnableViewState="true" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
        <asp:PlaceHolder ID="phFormContent" runat="server" EnableViewState="true">
            <asp:Panel ID="Panel1" runat="server" EnableViewState="true">
                <asp:Label ID="lblName" runat="server" Text="NAME:"></asp:Label>
                <asp:TextBox ID="txtName" runat="server"></asp:TextBox>
                <br /><br />
                <asp:Label ID="lblNumber" runat="server" Text="NUMBER:"></asp:Label>
                <asp:TextBox ID="txtNumber" runat="server"></asp:TextBox>
                <br /><br />
                <asp:Label ID="lblAddress" runat="server" Text="ADDRESS:"></asp:Label>
                <asp:TextBox ID="txtAddress" runat="server"></asp:TextBox>
                <br /><br />
                <asp:Label ID="lblCompany" runat="server" Text="COMPANY:"></asp:Label>
                <asp:TextBox ID="txtCompany" runat="server"></asp:TextBox>
                <hr />
                <br /><br />
            </asp:Panel>
        </asp:PlaceHolder>
        <br /><br />
        <asp:Button ID="btnAddForm" runat="server" Text="Add Form" OnClick="btnAddForm_Click" />
        <asp:Button ID="btnSubmitForms" runat="server" Text="Submit Forms" OnClick="btnSubmit_Click" />
    </form>
</body>
</html>

Upvotes: 0

Views: 1630

Answers (1)

LUKE
LUKE

Reputation: 1375

In your btnAddForm_Click method, you want to make sure you give the fields a consistent control id such as:

// Create Text Boxes
TextBox txtName = new TextBox();
txtName.ID = "txtName";
TextBox txtNumber = new TextBox();
txtNumber.ID = "txtNumber";
TextBox txtAddress = new TextBox();
txtAddress.ID = "txtAddress";
TextBox txtCompany = new TextBox();
txtCompany.ID = "txtCompany";

Then in your btnSubmitForm_Click method, you can grab all the panels within your placeholder via Linq such as:

var panelFields = (from panel in phFormContent.Controls.OfType<Panel>()
                                let txtName = panel.FindControl<TextBox>("txtName")
                                let txtNumber = panel.FindControl<TextBox>("txtNumber")
                                let txtAddress = panel.FindControl<TextBox>("txtAddress")
                                let txtCompany = panel.FindControl<TextBox>("txtCompany")
                                select new
                                {
                                     Name = txtName.Text,
                                     Number = txtNumber.Text,
                                     Address = txtAddress.Text,
                                     Company = txtCompany.Text
                                }).ToList();


foreach (var fields in panelFields)
{
    Response.Write(string.Format("{0} {1} {2} {3}<br />", fields.Name, fields.Number, fields.Address, fields.Company));
}

Update

I just wanted to note this for future googlers since I misread the question and there was another issue because the page lifecycle dictates you have to recreate dynamic controls on postback:

Edit for recreating dynamic controls on postback

I'm just showing the main methods that were changed from above.

// creates 1 panel on the first load
protected void Page_Load(object sender, EventArgs e)
{
    if (!Page.IsPostBack)
    {
        FormCount = 1;
    }
    CreatePanels();
}

// first line is added to grab the all panels in phFormContent before passing to the pdf creation routine
protected void btnSubmit_Click(object sender, EventArgs e)
{
    panels = phFormContent.Controls.OfType<Panel>().ToList();
    PDFDocument form = CreatePDF(this);

    Response.ContentType = "application/x-msdownload";
    Response.AddHeader("content-disposition", "attachment;filename=form.pdf");
    form.Save(Response.OutputStream);
    Response.Flush();
    Response.End();
}

// when adding we just inc the form counter and add a panel
protected void btnAddForm_Click(object sender, EventArgs e)
{
    FormCount = FormCount + 1;
    CreatePanel();
}

// routine to create the form panels
protected void CreatePanels()
{
    for (int i = 0; i < FormCount; i++)
    {
        CreatePanel();
    }
}
// this just creates a panel now and appends to placeholder;
protected void CreatePanel()
{
    // Create Labels
    Label lblName = new Label();
    lblName.Text = "NAME:";
    Label lblNumber = new Label();
    lblNumber.Text = "NUMBER:";
    Label lblAddress = new Label();
    lblAddress.Text = "ADDRESS:";
    Label lblCompany = new Label();
    lblCompany.Text = "COMPANY:";

    // Create Text Boxes
    TextBox txtName = new TextBox();
    TextBox txtNumber = new TextBox();
    TextBox txtAddress = new TextBox();
    TextBox txtCompany = new TextBox();

    // Create submit button
    Button btnSubmit = new Button();
    btnSubmit.Text = "SUBMIT";

    // Create panel and add controls
    Panel pnlForm = new Panel();
    pnlForm.EnableViewState = true;
    pnlForm.Controls.Add(lblName);
    pnlForm.Controls.Add(txtName);
    pnlForm.Controls.Add(new LiteralControl("<br /><br />"));
    pnlForm.Controls.Add(lblNumber);
    pnlForm.Controls.Add(txtNumber);
    pnlForm.Controls.Add(new LiteralControl("<br /><br />"));
    pnlForm.Controls.Add(lblAddress);
    pnlForm.Controls.Add(txtAddress);
    pnlForm.Controls.Add(new LiteralControl("<br /><br />"));
    pnlForm.Controls.Add(lblCompany);
    pnlForm.Controls.Add(txtCompany);
    pnlForm.Controls.Add(new LiteralControl("<hr />"));
    pnlForm.Controls.Add(new LiteralControl("<br /><br />"));
    phFormContent.Controls.Add(pnlForm);
}

// keeps track of how many panels we need to recreate.
public int FormCount
{
    get
    {
        if (ViewState["FormCount"] != null)
        {
            return (int)ViewState["FormCount"];
        }

        return 1;
    }
    set
    {
        ViewState["FormCount"] = value;
    }
}

With all of this in place the placeholder can be empty since the first page request adds the first panel:

<asp:PlaceHolder ID="phFormContent" runat="server" EnableViewState="true"></asp:PlaceHolder>

Upvotes: 0

Related Questions