Friday, July 13, 2007

ASP.NET Tip: Creating a Form Using PlaceHolder Controls

This example has a table called Parameters that has the following fields to help you determine how to build the table:


Primary key

Text to display next to control

Text field with the value 'String' or 'TF' in it (This will let you determine which control to show.)

You also could add extra fields indicating whether the field was required, a minimum/maximum length, and so forth, but this example is designed to show just the use of the PlaceHolder control.

You then can create a simple form like this one:

<%@ Page Language="C#" AutoEventWireup="true"
CodeFile="test.aspx.cs" Inherits="test" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
<html xmlns="" >
<head runat="server">
<title>Untitled Page</title>
<form id="form1" runat="server">
<asp:Repeater ID="rptFields" runat="server">
<td><%# Eval("Prompt") %>:</td>
<td><asp:PlaceHolder ID="plControl" runat="server" />
<input type="hidden" id="hdnFieldID"
value='<%# Eval("pkParameterID") %>' /></td>
<p align="center"><asp:LinkButton ID="btnSubmit"
runat="server">Submit Data</asp:LinkButton></p>

The code behind for this page looks like this:

public partial class test : System.Web.UI.Page
protected override void OnInit(EventArgs e)
rptFields.ItemDataBound +=
new RepeaterItemEventHandler(rptFields_ItemDataBound);

protected void Page_Load(object sender, EventArgs e)
Database db = new Database("(local)", "test", "sa", "dev1227");

private void AddControls(Database db)
DataTable dt = db.GetDataTableAdhoc("SELECT * FROM Parameters
ORDER BY pkParameterID");
rptFields.DataSource = dt;

void rptFields_ItemDataBound(object sender, RepeaterItemEventArgs e)
if (e.Item.ItemType != ListItemType.Item && e.Item.ItemType
!= ListItemType.AlternatingItem)

DataRow dr = ((DataRowView)e.Item.DataItem).Row;
PlaceHolder pl = (PlaceHolder)e.Item.FindControl("plControl");
switch (dr["DataType"].ToString().ToLower())
case "string":
TextBox txt = new TextBox();
txt.ID = "txtField" + dr["pkParameterID"].ToString();

case "tf":
CheckBox chk = new CheckBox();
chk.ID = "chkField" + dr["pkParameterID"].ToString();

As you see in the Page_Load routine, you need to load the dynamic controls every time—not just on the initial load of the page. I create an instance of my Database class, which encapsulates all my database code. Replace this with your favorite data access routine, but the sample uses a DataTable holding the contents of the Parameters table, which is bound against the Repeater control.

The ItemDataBound event does the bulk of the work here. It first determines that you are looking at an ItemTemplate (or AlternatingItemTemplate), and then it grabs the DataRow from the event arguments. This row holds the data type for the parameter, and a switch statement lets you get to the right area to add the right control. In each case, you instantiate a control of the appropriate type (TextBox vs. CheckBox) and then add it to the placeholder control (held in the pl variable). This causes the control to be displayed to the user.

If you wanted to give the control a default value, you'd need to check whether you were in postback mode prior to putting the value into the control or checking the box by default. If you didn't do this check, you'd essentially erase the user's input each time.

In the test page, you can type your data in and then press the Submit button. The Submit button will reload the page, which will show you that the values you typed in are repopulated automatically via the page's view state. This is a handy technique for building dynamic forms, especially those driven from user-configurable data.

No comments: