I had recently received a request from a dotnetcurry.com visitor about creating a dynamic table in ASP.NET. Now there are plenty of solutions available on the net. Most of the solutions I found, advised creating a dynamic table in the Page_Init() event, since the values of controls already exist in this event and can be used after a postback occurs. However this solution works best when the number of rows and columns are fixed, and then the table is created. But what if the number of rows and columns to be created, are to be accepted from the user, at runtime? In this article, we will explore how to accept the number of rows and columns from the user, create the table in the Page_Load() event and also retain the values on postback.
Whenever a control is added to the page dynamically, it is not persisted by default. That is because these ‘dynamically’ added controls are not automatically added to the page view state. Remember that for a dynamic control, the viewstate is available only after the post back occurs.
Note: ViewState maintains the state of controls, not the control tree. On postbacks, the control tree has to be recreated to make use of viewstate.
To elaborate on the above statement, the page is recreated each time it is posted back to the server. In other words, a new instance of the Page class is created and the class variables are set using the values from the ViewState. However during this recreation, the dynamically created controls are no longer available and hence the values are lost in the viewstate.
To override this behavior, you need to somehow make these controls available on each postback. One way of doing so, is to override the LoadViewState() method of the page. In the LoadViewState(), the view state data that had been saved from the previous page visit is loaded and recursively populated into the control hierarchy of the Page.
Why the LoadViewState()?
When the controls are added to page, it is done quiet early in the page event cycle. Hence the LoadViewState() gives you an ideal placeholder to recreate the controls. Since the LoadViewState() method is called before the Page_Load() event, re-adding controls in this method assures that the controls can be access and manipulated by the time any event occurs on them.
Why have you chosen to create and recreate the controls on Page_Load() instead of Page_Init()?
You can create dynamic controls in either the Page_Init() or Page_Load().
However as already explained in the introduction, we would be accepting the rows and columns from the user to create the table. Since these values would be available only after the user has entered the values in the two textboxes and clicked the button to cause a postback, the best suitable place is the Page_Load().
The second reason of choosing Page_Load() over Page_Init() is that in this example, we will be setting a flag on the ViewState to determine if the dynamic table has to be recreated or not. ViewState values are available during the Page_Load() and ‘not’ during the Page_Init() event.
Let us see some code now.
Step 1: Create a new ASP.NET application. Add two textboxes (txtRows and txtCols) and a button (btnGenerate) control to the page. The two textboxes will accept the number of Rows and Columns from the user. Based on the input in the textboxes, the table will be created dynamically on the button click. Also add a container element (to position the table) and a button (btnPost) to cause a postback the second time, once the table data is manipulated.
The mark up will look similar to the following:
<form id="form1" runat="server">
Rows: <asp:TextBox ID="txtRows" runat="server" Width="30px"> </asp:TextBox> <br />
Cols: <asp:TextBox ID="txtCols" runat="server" Width="30px"></asp:TextBox>
<asp:Button ID="btnGenerate" OnClick="btnGenerate_Click" runat="server" Text="Generate" /> <br /> <br />
<asp:PlaceHolder ID="PlaceHolder1" runat="server"></asp:PlaceHolder>
<asp:Button ID="btnPost" runat="server" OnClick="Button1_Click" Text="Cause Postback" />
Step 2: The number of rows and columns for the table is to be taken from the user. For this purpose, we will accept the values in the Page_Load() event. We will create properties for both the rows and columns and store in the ViewState so that the data is available on the postback.
// Rows property to hold the Rows in the ViewState
protected int Rows
return ViewState["Rows"] != null ? (int)ViewState["Rows"] : 0;
ViewState["Rows"] = value;
// Columns property to hold the Columns in the ViewState
protected int Columns
return ViewState["Columns"] != null ? (int)ViewState["Columns"] : 0;
ViewState["Columns"] = value;
protected void Page_Load(object sender, EventArgs e)
// Run only once a postback has occured
//Set the Rows and Columns property with the value
//entered by the user in the respective textboxes
this.Rows = Int32.Parse(txtRows.Text);
this.Columns = Int32.Parse(txtCols.Text);
' Rows property to hold the Rows in the ViewState
Protected Property Rows() As Integer
If Not ViewState("Rows") Is Nothing Then
Set(ByVal value As Integer)
ViewState("Rows") = value
' Columns property to hold the Columns in the ViewState
Protected Property Columns() As Integer
If Not ViewState("Columns") Is Nothing Then
Set(ByVal value As Integer)
ViewState("Columns") = value
Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs)
' Run only once a postback has occured
If Page.IsPostBack Then
'Set the Rows and Columns property with the value
'entered by the user in the respective textboxes
Me.Rows = Int32.Parse(txtRows.Text)
Me.Columns = Int32.Parse(txtCols.Text)
Observe over here that the values of the Rows and Columns properties are set only after the user enters the values and clicks the button, which causes the postback. That is the reason why we are checking against the If(Page.IsPostBack).
Step 3: On the button click, create the dynamic table as shown below. The code has been commented to help you understand.
protected void btnGenerate_Click(object sender, EventArgs e)
private void CreateDynamicTable()
// Fetch the number of Rows and Columns for the table
// using the properties
int tblRows = Rows;
int tblCols = Columns;
// Create a Table and set its properties
Table tbl = new Table();
// Add the table to the placeholder control
// Now iterate through the table and add your controls
for (int i = 0; i < tblRows; i++)
TableRow tr = new TableRow();
for (int j = 0; j < tblCols; j++)
TableCell tc = new TableCell();
TextBox txtBox = new TextBox();
txtBox.Text = "RowNo:" + i + " " + "ColumnNo:" + " " + j;
// Add the control to the TableCell
// Add the TableCell to the TableRow
// Add the TableRow to the Table
// This parameter helps determine in the LoadViewState event,
// whether to recreate the dynamic controls or not
ViewState["dynamictable"] = true;
Protected Sub btnGenerate_Click(ByVal sender As Object, ByVal e As EventArgs)
Private Sub CreateDynamicTable()
' Fetch the number of Rows and Columns for the table
' using the properties
Dim tblRows As Integer = Rows
Dim tblCols As Integer = Columns
' Create a Table and set its properties
Dim tbl As Table = New Table()
' Add the table to the placeholder control
' Now iterate through the table and add your controls
For i As Integer = 0 To tblRows - 1
Dim tr As TableRow = New TableRow()
For j As Integer = 0 To tblCols - 1
Dim tc As TableCell = New TableCell()
Dim txtBox As TextBox = New TextBox()
txtBox.Text = "RowNo:" & i & " " & "ColumnNo:" & " " & j
' Add the control to the TableCell
' Add the TableCell to the TableRow
' Add the TableRow to the Table
' This parameter helps determine in the LoadViewState event,
' whether to recreate the dynamic controls or not
ViewState("dynamictable") = True
Step 4: The last piece of code is to determine whether to recreate the controls based on the ViewState[“dynamictable”]. Here’s how it works. The first time the page loads, the LoadViewState() does not execute. The ViewState[“dynamictable”] flag is initialized once the button click occurs. When the postback occurs on the second button click (btnPost), it is then that our code written for overriding the LoadViewState() runs. The base.LoadViewState() instantiates the ViewState object. We then check the value of our ViewState flag, and based on the result, re-create our Table.
// Check the ViewState flag to determine whether to
// rebuild your table again
protected override void LoadViewState(object earlierState)
if (ViewState["dynamictable"] == null)
' Check the ViewState flag to determine whether to
' rebuild your table again
Protected Overrides Sub LoadViewState(ByVal earlierState As Object)
If ViewState("dynamictable") Is Nothing Then
That’s it. Run the code. Enter the number of Rows and Columns to be created and click the Generate button to create the table. Since the table contains textboxes in each cell, manipulate the text inside the table and hit the ‘Cause Postback’ button. Even after the postback, the values of the table control are retained.
Note: If multiple controls of the same type are created, remember to create them with the same ID's.
I hope you now have a good idea now of how to create controls dynamically. You can adopt a similar approach to create any dynamic control in your application. I hope the article was useful and I thank you for viewing it.
The entire source code of this article can be downloaded over here
This article has been editorially reviewed by Suprotim Agarwal.
C# and .NET have been around for a very long time, but their constant growth means there’s always more to learn.
We at DotNetCurry are very excited to announce the The Absolutely Awesome Book on C# and .NET. This is a 500 pages concise technical eBook available in PDF, ePub (iPad), and Mobi (Kindle).
Organized around concepts, this eBook aims to provide a concise, yet solid foundation in C# and .NET, covering C# 6.0, C# 7.0 and .NET Core, with chapters on the latest .NET Core 3.0, .NET Standard and the C# 8.0 (final release) too. Use these concepts to deepen your existing knowledge of C# and .NET, to have a solid grasp of the latest in C# and .NET OR to crack your next .NET Interview.
Click here to Explore the Table of Contents or Download Sample Chapters!