Click here to Skip to main content
15,897,187 members
Articles / Web Development / ASP.NET
Article

Using a drop-down list to dynamically load user controls in a repeater

Rate me:
Please Sign up or sign in to vote.
2.33/5 (3 votes)
9 May 20074 min read 40.3K   11  
Changing the visibility of user controls based on drop-down values in a repeater

Introduction

As a developer I sometimes find myself going for the harder solution when an easier one will do. This was certainly one of those times. I needed a way to change what information could be entered based on the value of a drop-down list. This was my thinking:

First, different information was needed depending on the drop-down value selected, so a user control for each different type of information was required.

Second, these user controls needed to be shown based on the selected drop-down value in the same repeater item container. This was so you could show that additional information was needed near the control that determined what type of information was required. I've put the code for this below rather than attach it - it didn't seem to rate a demo.

Problems I encountered were:

  1. if you try to dynamically load a user control using AddAt then the repeater adds it to that place - replacing whatever control was at that position in the control hierarchy
  2. there was no nice way of finding the index of the control in the hierarchy
  3. the controls had to be initialised before showing the list for the first time
  4. since there could be any number of drop-down controls in the repeater - and since they would have different things selected - we needed a way to find the control and make it visible

The first problem was resolved by not dynamically loading the control but setting its Visible property to false. That way you don't have to keep track of where it is in the control hierarchy; you can just use its Visible property. The second problem was solved using a regex pattern to look for its control number in ControlID - i.e. looking for ctl1, ctl2 and getting the control index this way. These are both far from ideal. Problem three was solved by placing the initialisation code in the repeater databound event. The fourth problem required use of the selectedindexchanged event of the drop-down.

My advice is to go and have a coffee and write down exactly what you need to do in order to achieve some developer goal. Then work through each of those things systematically. Design only as much as you need to in order to get the job done. (Unless you're an agile developer :)

If you can think of a better way that uses the observer pattern - where each user control subscribes to the events of the extended control - or some other approach, I'd love to hear about it.

Background

Originally I took a more complex approach and created an extended drop-down list with additional events and properties. I created two user controls to load dynamically, based on events firing on changes to this property. I coupled this to use of the observer pattern. I also thought of going down the route of creating a custom template that implements ITemplate. Both attempts came up with too many problems to overcome in what was a very simple issue, so I went back to the way I thought of first even though it didn't feel nice and certainly wasn't smart.

Using the code

  1. create a web project in VS.NET 2003 or 2005
  2. create two user controls using the default name
  3. add a webform1.aspx and wire up - you'll need to change the register sections to suit
  4. add a project to this solution and add the business classes - in this case licences, but could also be ASCX, etc

Licences code is a very simple collection. Change it to using a generic dictionary if you are using C# 2.0.

Licences.cs

C#
namespace ControlLibrary1
{
    using System;
    using System.Collections;

    public class Licences : CollectionBase 
    {
        //default .ctor
        public Licences(){}

        public int Add(Licence Licence) 
        {
            return List.Add(Licence);
        }

        public void Remove(Licence Licence) 
        {
            List.Remove(Licence);
        }

        public class Licence 
        {
            private string LicenceName;

            public Licence(string Name) 
            {
                LicenceName = Name;
            }

            public string Name 
            {
                get 
                {
                    return LicenceName;
                }
                set 
                {
                    LicenceName = value;
                }
            }
        }
    }
}

WebForm1.aspx.cs

C#
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Text.RegularExpressions;
using System.Drawing;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using WindowsControlLibrary1;

namespace WebApplication1
{
    /// <summary>
    /// TODO: quicker way & better way (e.g. Observer pattern, ITemplate)
    /// </summary>

    public class WebForm1 : System.Web.UI.Page
    {
        protected System.Web.UI.WebControls.Repeater Repeater1;
        protected ArrayList items;
        protected DropDownList ddlExtended;
        protected int index = 0;
        protected WebUserControl1 webUserControl1;
        protected WebUserControl2 webUserControl2;

        private void Page_Load(object sender, System.EventArgs e)
        {
            Licences.Licence p1 = new Licences.Licence("Licence1");
            Licences.Licence p2 = new Licences.Licence("Licence2");
            Licences.Licence p3 = new Licences.Licence("Licence3");
            Licences.Licence p4 = new Licences.Licence("Licence4");
            Licences Licences = new Licences();
            Licences.Add(p1);
            Licences.Add(p2);
            Licences.Add(p3);
            Licences.Add(p4);

            if (!IsPostBack)
            {
                Repeater1.DataSource = Licences;
                Repeater1.DataBind();
            }
        }

        #region Web Form Designer generated code
        override protected void OnInit(EventArgs e)
        {        
            InitializeComponent();
            base.OnInit(e);
        }
        
        private void InitializeComponent()
        {    
            this.Load += new System.EventHandler(this.Page_Load); 
            this.Repeater1.ItemDataBound+=new 
                RepeaterItemEventHandler(Repeater1_ItemDataBound);
        }
        #endregion

        private void Repeater1_ItemDataBound(object sender, 
            RepeaterItemEventArgs e)
        {
            DropDownList list = 
                            (DropDownList)e.Item.FindControl("ddlExtended");
            if(list!=null)
            {
                items = new ArrayList();
                items.Add("One Country");
                items.Add("Region");
                list.DataSource = items;
                list.DataBind();
            }

            WebUserControl1 cont1 = 
                       (WebUserControl1)e.Item.FindControl("webUserControl1");
            WebUserControl2 cont2 = 
                       (WebUserControl2)e.Item.FindControl("webUserControl2");
            if(cont1 != null | cont2 != null)
            {
                if(list.SelectedValue == "One Country")
                {
                    cont1.Visible = true;
                }
                else
                {
                    cont2.Visible = true;
                }
            }
        }

        protected void ddlExtended_SelectedIndexChanged(object sender, 
                    EventArgs e)
        {
                WebUserControl1 control1 = 
                  (WebUserControl1)Repeater1.Items[GetControlIndex(
                  (
                  (
             (DropDownList)sender).ClientID))].FindControl("webUserControl1");
                WebUserControl2 control2 = 
                  (WebUserControl2)Repeater1.Items[GetControlIndex(
                  (
                  (
             (DropDownList)sender).ClientID))].FindControl("webUserControl2");
                    
            if(((DropDownList)sender).SelectedValue == "One 
                            Country")
            {
                control1.Visible = true;
                control2.Visible = false;
            }
            else
            {
                control2.Visible = true;
                control1.Visible = false;
            }
        }

        public int GetControlIndex(String controlID)
        {
            Regex regex =new Regex("([0-9.*])", 
                            RegexOptions.RightToLeft);
            Match match = regex.Match(controlID);
            
            return Convert.ToInt32(match.Value);
        } 

    }
}

WebForm1.aspx

  1. use register tags for each user control
  2. set usercontrol visible = false and add to the itemtemplate of the repeater
  3. add Databinder.eval and bind to each public property in the collection
  4. add a drop-down list to itemtemplate and set Onselectedindexchanged to the code that will handle it and set autopostback to true
ASP.NET
<%@ Page language="c#" Codebehind="WebForm1.aspx.cs" AutoEventWireup="false" 
    Inherits="WebApplication1.WebForm1" %>
<%@ Register tagprefix="tp" Namespace="WindowsControlLibrary1" 
    Assembly="WindowsControlLibrary1" %>
<%@ register tagprefix="uc1" tagname="webusercontrol1" 
    src="WebUserControl1.ascx" %>
<%@ register tagprefix="uc2" tagname="webusercontrol2" 
    src="WebUserControl2.ascx" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<HTML>
<HEAD>
    <title>WebForm1</title>
</HEAD>
<body>
    <form id="Form1" method="post" runat="server">

        <asp:Repeater id="Repeater1" runat="server">
            <ItemTemplate>
                <b>
                    <%#DataBinder.Eval(Container.DataItem, "Name")%>
                </b>
                <br />
                <asp:DropDownList id="ddlExtended" AutoPostBack="True" 
                    OnSelectedIndexChanged="ddlExtended_SelectedIndexChanged"
                    Runat="server"></asp:DropDownList><br/>
                <uc1:webusercontrol1 id="webUserControl1" Visible="false" 
                    OnMarketChange="" runat="server"></uc1:webusercontrol1>
                <uc2:webusercontrol2 id="webUserControl2" Visible="false" 
                    OnMarketChange="" runat="server"></uc2:webusercontrol2>    
                <br/>                
            </ItemTemplate>

        </asp:Repeater>
    </form>
</body>
</HTML>

Points of interest

I could find an easy (and nice!) way of getting the index of the control in the control hierarchy. It would be a huge oversight to not have it - so I guess I've missed it. I assume you don't have to code it by creating a new drop-down list with extended event arguments that have this information in it. The FindControl and Visible properties were pretty much the only things needed for this problem in the end. I'd like to hear what your improvements would be.

History

This is NOT nice code, it is NOT good code - but it got the job done. I'll add other ways of doing this that will demonstrate best-practice at a later stage.

  • May 9, 2007 - Original version posted

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United Kingdom United Kingdom
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
-- There are no messages in this forum --