This is a brilliant little server control from Rob van der Veer (found here) – which I’ve found lots of uses for but keep having to spend time tracking it down…so here it is, mainly for me but partly for you too  

///

    /// A templated databound repeater that dynamically determines which

    /// template to use based on the class of the object in the collection.

    ///

    [ParseChildren(false)]

    [ControlBuilder(typeof(ObjectRepeaterControlBuilder))]

    public class ObjectRepeater : System.Web.UI.Control, System.Web.UI.INamingContainer, IParserAccessor

    {

        private Hashtable _templates = new Hashtable();

        private string _defaultTemplateName = null;

        private IEnumerable _dataSource;

 

        public ObjectRepeaterDetermineTemplateDelegate DetermineTemplate;

        public event RepeaterItemEventHandler ItemCreated;

        public event RepeaterItemEventHandler ItemDataBound;

        public event RepeaterCommandEventHandler ItemCommand;

 

        #region Properties

 

        public string DefaultTemplate

        {

            get

            {

                return _defaultTemplateName;

            }

            set

            {

                _defaultTemplateName = value;

            }

        }

 

        public virtual IEnumerable DataSource

        {

            get

            {

                return _dataSource;

            }

            set

            {

                _dataSource = value;

            }

        }

 

        public ControlCollection Items

        {

            get

            {

                return this.Controls;

            }

        }

 

        #endregion

 

        #region Methods

 

        public void OnItemDataBound(RepeaterItemEventArgs e)

        {

            if(this.ItemDataBound != null)

                this.ItemDataBound(this, e);

        }

 

        public void OnItemCreated(RepeaterItemEventArgs e)

        {

            if(this.ItemCreated != null)

                this.ItemCreated(this, e);

        }

 

        public void OnItemCommand(RepeaterCommandEventArgs e)

        {

            if(this.ItemCommand != null)

                this.ItemCommand(this, e);

        }

 

        public override void DataBind()

        {

            // Controls with a data-source property perform their

            // custom data binding by overriding DataBind to

            // evaluate any data-binding expressions on the control   

            // itself.

            base.OnDataBinding(EventArgs.Empty);

 

            // Reset the control's state.

            Controls.Clear();

            ClearChildViewState();

 

            // Create the control hierarchy using the data source.

            CreateControlHierarchy(true);

            ChildControlsCreated = true;

 

            TrackViewState();

 

        }

 

        protected override void CreateChildControls()

        {

            Controls.Clear();

 

            if (ViewState["ItemCount"] != null)

            {

                // Create the control hierarchy using the view state,

                // not the data source.

                CreateControlHierarchy(false);

            }

        }

 

        ///

        ///

        ///

        /// True to create the hierarchy from the DataSource, False to create it from the ViewState.

        private void CreateControlHierarchy(bool useDataSource)

        {

            IEnumerable dataSource = null;

            int count = -1;

 

            if (useDataSource == false)

            {

                // ViewState must have a non-null value for ItemCount because this is checked

                //  by CreateChildControls.

                count = (int)ViewState["ItemCount"];

                if (count != -1)

                {

                    dataSource = new DummyDataSource(count);

                }

            }

            else

            {

                dataSource = this._dataSource;

            }

 

            if (dataSource != null)

            {

                int index = 0;

                count = 0;

                foreach (object dataItem in dataSource)

                {

                    CreateItem(index, ListItemType.Item, useDataSource, dataItem);

 

                    count++;

                    index++;

                }

            }

 

            if (useDataSource)

            {

                // Save the number of items contained for use in round trips.

                ViewState["ItemCount"] = ((dataSource != null) ? count : -1);

            }

        }

 

        private RepeaterItem CreateItem(int itemIndex, ListItemType itemType, bool dataBind, object dataItem)

        {

            RepeaterItem item = new RepeaterItem(itemIndex, itemType);

 

            RepeaterItemEventArgs e = new RepeaterItemEventArgs(item);

 

            //decide which template to use.

            string templateName = null;

 

            if(dataBind)

            {

                if(DetermineTemplate != null)

                {

                    templateName = this.DetermineTemplate(this, dataItem);

                    ViewState["templateName" + itemIndex.ToString()] = templateName;

                }

            }

            else

            {

                //determine template to use from viewState;

                templateName = (string)ViewState["templateName" + itemIndex.ToString()];

            }

 

            if(templateName == null)

                templateName = this.DefaultTemplate;

            ObjectTemplate dynamicTemplate = (ObjectTemplate)_templates[templateName];

 

 

            //Must exist.

            dynamicTemplate.ItemTemplate.InstantiateIn(item);

 

            if (dataBind)

            {

                item.DataItem = dataItem;

            }

            OnItemCreated(e);

            this.Controls.Add(item);

 

            if (dataBind)

            {

                item.DataBind();

                OnItemDataBound(e);

 

                item.DataItem = null;

            }

 

            return item;

        }

 

        protected override bool OnBubbleEvent(object source, EventArgs e)

        {

            // Handle events raised by children by overriding OnBubbleEvent.

 

            bool handled = false;

 

            if (e is CommandEventArgs)

            {

                RepeaterCommandEventArgs ce = (RepeaterCommandEventArgs)e;

 

                OnItemCommand(ce);

                handled = true;

            }

 

            return handled;

        }

 

 

        ///

        /// This member overrides Control.AddParsedSubObject.

        /// it catches the contents of each item.

        ///

        protected override void AddParsedSubObject(object obj)

        {

            if(obj is ObjectTemplate)

            {   

                ObjectTemplate template = (ObjectTemplate)obj;

                _templates.Add(template.Name, template);

                return;

            }

            else

            {

                if(!(obj is LiteralControl))

                    throw new Exception("ObjectRepeater can only have children of type 'ObjectTemplate'");

            }

            base.AddParsedSubObject(obj);

        }   

 

        #endregion

    }   

 

    public delegate string ObjectRepeaterDetermineTemplateDelegate(object sender, object dataItem);

 

    #region Control Builder

    ///

    /// Interacts with the parser to build a PanelBar control.

    ///

    public class ObjectRepeaterControlBuilder : ControlBuilder

    {

        ///

        /// This member overrides ControlBuilder.GetChildControlType.

        ///

        public override Type GetChildControlType(string tagName, IDictionary attributes)

        {

            // check is the tag is an TabStripPanelItem tag

            if (tagName.ToLower().IndexOf("objecttemplate") >= 0)

            {

                System.Diagnostics.Trace.WriteLine(tagName);

 

                // yes, so return TabStripPanelItem type

                return typeof(ObjectTemplate);

 

            }

            return null;

        }

 

        public override void AppendLiteralString(string s)

        {

            //empty implementation to drop literal content.

            System.Diagnostics.Trace.WriteLine(s);

        }

 

    }

    #endregion

 

    [ParseChildren(true)]

    public class ObjectTemplate : Control

    {

        private string _name;

        private ITemplate _itemTemplate;

 

        [TemplateContainer(typeof(RepeaterItem))]

        public ITemplate ItemTemplate

        {

            get { return _itemTemplate; }

            set { _itemTemplate = value; }

        }

 

        public string Name

        {

            get { return _name; }

            set { _name = value; }

        }

    }

    internal sealed class DummyDataSource : ICollection

    {

        private int dataItemCount;

        public DummyDataSource(int dataItemCount)

        {

            this.dataItemCount = dataItemCount;

        }

        public int Count

        {

            get

            {

                return dataItemCount;

            }

        }

        public bool IsReadOnly

        {

            get

            {

                return false;

            }

        }

        public bool IsSynchronized

        {

            get

            {

                return false;

            }

        }

        public object SyncRoot

        {

            get

            {

                return this;

            }

        }

        public void CopyTo(Array array, int index)

        {

            for (IEnumerator e = this.GetEnumerator(); e.MoveNext();)

                array.SetValue(e.Current, index++);

        }

        public IEnumerator GetEnumerator()

        {

            return new BasicDataSourceEnumerator(dataItemCount);

        }

 

        private class BasicDataSourceEnumerator : IEnumerator

        {

            private int count;

            private int index;

            public BasicDataSourceEnumerator(int count)

            {

                this.count = count;

                this.index = -1;

            }

            public object Current

            {

                get

                {

                    return null;

                }

            }

            public bool MoveNext()

            {

                index++;

                return index < count;

            }

            public void Reset()

            {

                this.index = -1;

            }

        }

    }