When adding controls dynamically to a page, it is important to do so on every page load or they no longer exist. For example, the code below reads contact records from a database and creates LinkButtons for each contact. It also encodes the LinkButtons with a CommandArgument set to the contact's unique identifier and wires it up to an event handler before adding to the page via a panel.
protected override void OnInit(EventArgs e)
{
SetupContacts();
}
private void SetupContacts()
{
.
.
.
foreach (ContactsRow row in contactsTable.Rows)
{
LinkButton link = new LinkButton();
link.Text = row.FirstName + " " + row.LastName;
link.CommandArgument = row.ContactID.ToString();
link.Click += new EventHandler(link_Click);
panel1.Controls.Add(link);
}
A problem can occur when one of the dynamically created controls is deleted or moved on the page. This is due to the fact that ViewState connects its information to a control by the control’s ID. If you do not specify an ID, .Net creates the ID dynamically (e.g., ctl14, ctl19, etc.). This will cause issues if you create a control on your page that deletes or moves the dynamically created control(s). This is because it will change the ID of the control(s) after ViewState has been loaded and connected to the previous ID(s). This is caused because child controls have their events fire after the ViewState is loaded for the page and tied to each controls’ ID. To illustrate, suppose we have a Button control that accomplishes the following:
protected void Delete_Click(object sender, EventArgs e)
{
DeleteContact(1);
SetupControls();
}
After clicking the Delete button, the deleted contact will disappear but none of the LinkButtons will fire properly on the next click. They will begin to work after you click one of the LinkButtons once, because the corresponding postback will reconnect the LinkButtons’ IDs to the ViewState successfully. The key to getting around this issue is to be sure to add a unique identifier when you dynamically create the controls. This will keep the ViewState info connected to the controls even if some of them are moved or deleted. See the bolded code in the modified SetupContacts code below.
private void SetupContacts()
{
foreach (ContactsRow row in contactsTable.Rows)
{
LinkButton link = new LinkButton();
link.Text = row.FirstName + " " + row.LastName;
link.CommandArgument = row.ContactID.ToString();
link.Click += new EventHandler(link_Click);
link.ID = "contactLink" + row.ContactID.ToString();
panel1.Controls.Add(link);
}
}