Introduction

The first question you should be asking is why do you want user controls in the first place. And the answer is usually one of the following three options


•You don’t know the number or type of controls that you will need until runtime. This is possibly the most common reason to use dynamic controls.
•You have a large number of the same type of control that you need to add to the application and you don’t want to spend the time manually putting them onto the design surface. Most people will be familiar with this because I doubt you have ever manually created 100 list items in a DataList for example
•And finally its because of an architectural decision such as a custom server control and you have no choice.
 
The next question is what is the different between static and dynamic controls. Well firstly static controls  

•Can be added to the design surface at design time
•They can be programmed against with intellisense because they are in the .designer.cs file
•And finally they survive post back and the page life cycle.

On the other hand dynamic controls

•Dynamic controls are added to the page control tree at runtime
•They cannot be programmed against using intellisense in the traditional way. You can declare an object of the type of control and bring it to your instance of the control and program against it in that way
•Dynamic controls do not survive the post back unless you give them a place to stay. And that is the crux of the problem involving dynamic controls.
 
Now that we know what the issue is how do we solve it. Normally when you are using dynamic controls you add the controls to a placeholder and this works ok to a point. A better way of doing it is to use a repeater with a placeholder in the ItemTemplate and use custom methods of the ItemCreated and ItemCommand events. There are a few good reasons for this.

•You can databind your list to your repeater allowing to quickly create a lot of controls
•You can make your controls have databinding properties.
•You can use the fact that the repeater is IEnummerable to get the information in and out
•The ItemCommand event allows you to handle events from your controls
•And finally it gives you a way to easily store state by using the repeater.

Building the sample

The sample will run out of the box, there is 4 projects in the solution including 2 database projects.
For users using SQL Server Express, please check the connection string in the 2 database projects by checking the properties of each project and verifying the Target connection setting

Description

This sample code shows how to create dynamic user controls using a Repeater and databinding
Sample Person control
C#
Edit|Remove
public partial class PersonControl : UserControl, IUserControl<Person> 
    { 
        private Person _data; 
 
        /// <summary> 
        /// Fired when the control is data bound.  
        /// Puts the information into the appropriate text boxes. 
        /// Change this methods contents to handle how you want the data to be displayed. 
        /// </summary> 
        public override void DataBind() 
        { 
            //Checks to Data property in the control has information 
            if (Data != null) 
            { 
                //Set the textboxs Text property to the different values in the Person entity 
                txtFirstName.Text = Data.FirstName; 
                txtLastName.Text = Data.LastName; 
 
                //Important to set this private variable to null so that 
                //when the control is asked for its data, its supplies the most 
                //current data rather than cached 
                _data = null; 
            } 
 
            base.DataBind(); 
        } 
 
 
        public Person Data 
        { 
            //Uses the null coalescing operator ?? to return either the current value of _data or  
            //create a new one with the values in the textboxes on the control 
            get { return _data ?? ( 
                _data = new Person 
                            { 
                                FirstName = txtFirstName.Text,  
                                LastName = txtLastName.Text 
                            }); } 
            //Sets the _data object to the current value 
            //Required because it is used in line 30 
            set { _data = value; } 
        } 
 
        /// <summary> 
        /// Handles the onClick event of lnkRemove link button 
        /// </summary> 
        /// <param name="sender"></param> 
        /// <param name="e"></param> 
        protected void RemoveItem(object sender, EventArgs e) 
        { 
            //Bubble a new event up from the control to the hosting control or page. 
            //If you wanted the page to handle this, you would need to make a new EventHandler on 
            //the control to ensure the the page could subscribe to the event 
            RaiseBubbleEvent(thisnew CommandEventArgs("Remove"null)); 
        } 
    }
 Sample databinding of this control using a repeater
 
C#
Edit|Remove
/// <summary> 
        /// Handles the ItemCreated Event of the repeater.  
        /// Each item when its created, you want to make sure it comes out as a specific user control. 
        /// This method supplies specific data to a generic method which perfoms the loading and databinding of the correct control. 
        /// </summary> 
        /// <param name="sender"></param> 
        /// <param name="e"></param> 
        private void RptPeopleItemCreated(object sender, RepeaterItemEventArgs e) 
        { 
            AddControl<Person>("~/UserControls/PersonControl.ascx""phPerson", e); 
        }
 The generic AddControl method. Each user control inherits from the IUserControl
 
 
C#
Edit|Remove
   /// <summary> 
        /// This method loads the specific user control and binds the data to it, or loads a blank 
        /// control is there is no data to be bound to it. 
        /// </summary> 
        /// <typeparam name="T">The entity type</typeparam> 
        /// <param name="controlPath">The path to the user control you want to load</param> 
        /// <param name="controlName">The name of the placeholder in the repeater that will hold the control</param> 
        /// <param name="e">RepeaterItemEventArgs. The DataItem will be cast to the entity type</param> 
        private void AddControl<T>(string controlPath, string controlName, RepeaterItemEventArgs e) 
        { 
            //First cast the DataItem to the correct type. 
            var data = (T)e.Item.DataItem; 
 
            //Check to see if data has any values in it. If it does, load a control and bind the data to that control. 
            if (!Equals(data, default(T))) 
            { 
                //Get the placeholder tht will be add the control to. 
                var placeHolder = e.Item.FindControl(controlName); 
                //Load the control specified in the parameters. 
                var control = (IUserControl<T>)LoadControl(controlPath); 
                //Bind the data to the control. Check the specific control to see what it does with it 
                control.Data = data; 
                //Add the control to the placeholder.  
                placeHolder.Controls.Add((Control)control); 
            } 
            else 
            { 
                //Annon method that handles the item load. This is used if the data has no values in it, ie a newly added control. 
                e.Item.Load += (sender, ea) => 
                                   { 
                                       //Cast the sender as a RepeaterItem 
                                       var literal = (RepeaterItem)sender; 
                                       //Find the placeholder that we need within the RepeaterItem 
                                       var placeHolder = literal.FindControl(controlName); 
                                       //Load an instance of the control that we need 
                                       var control = (IUserControl<T>)LoadControl(controlPath); 
                                       //Add the control to the placeholder 
                                       placeHolder.Controls.Add((Control)control); 
                                   }; 
            } 
        }