Click here to Skip to main content
6,634,665 members and growing! (14,973 online)
Email Password   helpLost your password?
Web Development » ASP.NET » General     Beginner

Using a dropdownlist to dynamically load user controls in a repeater

By neuralSea8

change visibility of user controls based on value of drop-down in a repeater
C# 1.0, C# 2.0, Windows, .NET 1.1, .NET 2.0, ASP.NET, WebForms, VS.NET2003, VS2005, Dev
Posted:6 May 2007
Views:16,199
Bookmarked:7 times
Unedited contribution
Announcements
Loading...
 
Search    
Advanced Search
Add to IE Search
printPrint   add Share
      Discuss Discuss   Broken Article?Report  
2 votes for this article.
Popularity: 0.40 Rating: 1.33 out of 5
1 vote, 50.0%
1
1 vote, 50.0%
2

3

4

5

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 thought that this problem was harder than it was...ah well, live and learn.

I needed a way to change what information could be entered based on the value of a drop-down list. If the value of a drop-down was one thing different information needed to be entered compared with another.

This was my thinking:

1. different information was needed - so use a user control for each different type of information

2. show these user controls based on what the selected value of the dropdown was (the dropdown in the same repeater item container) - so that whatever information was relevant was closest to the control on which it depended.

(I've put the code below rather than attaching it - it didn't seem to rate a demo.)

I came across these problems:

  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 -I resolved this 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 it Visible property.
  2. no nice way of finding the index of the control in the hierarchy - I used a regex pattern to look for its control number in ControlID - i.e. looking for ctl1, ctl2 and getting the control index this way. (I suppose you could build a control tree structure and have each leaf have controlID, absolute index, relative index, name and gettypeof() - build tree using recursion - its better than a whole bunch of nested foreach loops...ugh!
  3. the controls had to initialised before showing the list for the first time (not really a problem but something that needed to be done in this case.) - easy enough to do - just place the initialisation code in the repeater databound event.
  4. since there could be any number of dropdown controls in the repeater - and since they will have different things selected then we needed a way to find the control and make visible - I used the SelectedIndexChanged event of the drop-down for this.

All of these solutions are both far from ideal.

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. And 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 an 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. Created two user controls to load dynamically based on events firing on changes to this property. And coupled this to using the Observer pattern. I also thought of going down the route of creating a custom template that implements ITemplate. Both attempts seemed came up with too many problems to overcome in what was a very simple problem - 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/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.

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.
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 l1 = new Licences.Licence("Licence1");
            Licences.Licence l2 = new Licences.Licence("Licence2");
            Licences.Licence l3 = new Licences.Licence("Licence3");
            Licences.Licence l4 = new Licences.Licence("Licence4");
            Licences Licences = new Licences();
            Licences.Add(l1);
            Licences.Add(l2);
            Licences.Add(l3);
            Licences.Add(l4);

            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 foreach user control
  2. set usercontrol visible=false and add to itemtemplate of the repeater
  3. add Databinder.eval and bind to each public property in collection
  4. add a dropdownlist to itemtemplate and set Onselectedindexchanged to the code that will handle it and set autopostback to true

(missing the register tags at the top and the ItemTemplate tags wrapping the dropdownlist and the user controls).

By the way, my advice is to always add events to your user controls - I think someone else on this site gave the same advice. I'd go one step further and say that custom objects should implement IFormattable, have a Notify event, throw custom errors and override ToString(), GetHashCode() and Equals() - 'cause not all code is perfect and it'll save you hours in debugging time (if you give enough information). Perhaps use something like GetHashCode(){return Mash(this);} - make sure that Equals and GetHashCode work together :-)

<html />
    <head></head>
        
    
    
        

<form id="Form1" method="post" runat="server">
            <repeater id="Repeater1" runat="server" />
                
                    
                        
                    
                    

                    <dropdownlist id="ddlExtended" runat="server" onselectedindexchanged="ddlExtended_SelectedIndexChanged" autopostback="True" />

                    <webusercontrol1 id="webUserControl1" runat="server" visible="false" />
                    <webusercontrol2 id="webUserControl2" runat="server" visible="false" />    
                    
                
                
            </repeater />
        </form>


    

Points of Interest

FindControl and Visible property 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 demonstate best-practice at a later stage.

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

About the Author

neuralSea8


Member

Occupation: Web Developer
Location: United Kingdom United Kingdom

Other popular ASP.NET articles:

Article Top
You must Sign In to use this message board.
FAQ FAQ 
 
Noise Tolerance  Layout  Per page   
  (Refresh) 
-- There are no messages in this forum --

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 6 May 2007
Editor:
Copyright 2007 by neuralSea8
Everything else Copyright © CodeProject, 1999-2009
Web10 | Advertise on the Code Project