Export (0) Print
Expand All

Templated Data-Bound Control Sample

.NET Framework 1.1

The TemplatedList control described in the following sample is a simplified version of the System.Web.UI.DataList control. It illustrates the following concepts.

  • Data binding, as described in Developing a Templated Data-Bound Control.

    DataSource is the data source for TemplatedList.

    ItemTemplate is the template property of TemplatedList.

    TemplatedListItem is the logical container for ItemTemplate. TemplatedListItem has a property, DataItem, that binds to each element of DataSource.

  • Event bubbling, as described in Bubbling an Event.

    TemplatedListItem bubbles command events from controls in the template to TemplatedList. TemplatedList exposes a bubbled event as an ItemCommand event. The TemplatedListCommandEventArgs class contains the data for the event.

  • Exposing styles from a control as described in Styles in Server Controls.

    TemplatedList exposes the ItemStyle, AlternatingItemStyle, and SelectedItemStyle properties of type System.Web.UI.WebControls.TableStyle.

  • Customizing state restoration using ViewState.

    TemplatedList overrides the SaveViewState and LoadViewState methods inherited from Control to customize the restoration of its Style properties.

  • Applying metadata attributes for design-time support. For information about design-time attributes, see Attributes and Design-Time Support. The designer (CustomControls.Design.TemplatedListDesigner) specified in the DesignerAttribute is implemented in the Web Forms Templated Data-Bound Control Designer Sample.

To build the sample, see the instructions in Server Control Samples.

// TemplatedList.cs.

namespace CustomControls {

    using System;
    using System.ComponentModel;
    using System.ComponentModel.Design;
    using System.ComponentModel.Design.Serialization;
    using System.Collections;
    using System.Diagnostics;
    using System.Web.UI;
    using System.Web.UI.WebControls;

    [
    DefaultEvent("SelectedIndexChanged"),
    DefaultProperty("DataSource"),
    Designer("CustomControls.Design.TemplatedListDesigner, CustomControls.Design", typeof(IDesigner))
    ]
    public class TemplatedList : WebControl, INamingContainer {

#region Statics and Constants
        private static readonly object EventSelectedIndexChanged = new object();
        private static readonly object EventItemCreated = new object();
        private static readonly object EventItemDataBound = new object();
        private static readonly object EventItemCommand = new object();
#endregion

#region Member variables
        private IEnumerable dataSource;
        private TableItemStyle itemStyle;
        private TableItemStyle alternatingItemStyle;
        private TableItemStyle selectedItemStyle;
        private ITemplate itemTemplate;
#endregion

#region Properties
        [
        Category("Style"),
        Description("The style to be applied to alternate items."),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
        NotifyParentProperty(true),
        PersistenceMode(PersistenceMode.InnerProperty),
        ]
        public virtual TableItemStyle AlternatingItemStyle {
            get {
                if (alternatingItemStyle == null) {
                    alternatingItemStyle = new TableItemStyle();
                    if (IsTrackingViewState)
                        ((IStateManager)alternatingItemStyle).TrackViewState();
                }
                return alternatingItemStyle;
            }
        }

        [
        Bindable(true),
        Category("Appearance"),
        DefaultValue(-1),
        Description("The cell padding of the rendered table.")
        ]
        public virtual int CellPadding {
            get {
                if (ControlStyleCreated == false) {
                    return -1;
                }
                return ((TableStyle)ControlStyle).CellPadding;
            }
            set {
                ((TableStyle)ControlStyle).CellPadding = value;
            }
        }

        [
        Bindable(true),
        Category("Appearance"),
        DefaultValue(0),
        Description("The cell spacing of the rendered table.")
        ]
        public virtual int CellSpacing {
            get {
                if (ControlStyleCreated == false) {
                    return 0;
                }
                return ((TableStyle)ControlStyle).CellSpacing;
            }
            set {
                ((TableStyle)ControlStyle).CellSpacing = value;
            }
        }

        [
        Bindable(true),
        Category("Data"),
        DefaultValue(null),
        Description("The data source used to build up the control."),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
        ]
        public IEnumerable DataSource {
            get {
                return dataSource;
            }
            set {
                dataSource = value;
            }
        }

        [
        Bindable(true),
        Category("Appearance"),
        DefaultValue(GridLines.None),
        Description("The grid lines to be shown in the rendered table.")
        ]
        public virtual GridLines GridLines {
            get {
                if (ControlStyleCreated == false) {
                    return GridLines.None;
                }
                return ((TableStyle)ControlStyle).GridLines;
            }
            set {
                ((TableStyle)ControlStyle).GridLines = value;
            }
        }

        [
        Category("Style"),
        Description("The style to be applied to all items."),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
        NotifyParentProperty(true),
        PersistenceMode(PersistenceMode.InnerProperty),
        ]
        public virtual TableItemStyle ItemStyle {
            get {
                if (itemStyle == null) {
                    itemStyle = new TableItemStyle();
                    if (IsTrackingViewState)
                        ((IStateManager)itemStyle).TrackViewState();
                }
                return itemStyle;
            }
        }

        [
        Browsable(false),
        DefaultValue(null),
        Description("The content to be shown in each item."),
        PersistenceMode(PersistenceMode.InnerProperty),
        TemplateContainer(typeof(TemplatedListItem))
        ]
        public virtual ITemplate ItemTemplate {
            get {
                return itemTemplate;
            }
            set {
                itemTemplate = value;
            }
        }

        [
        Bindable(true),
        DefaultValue(-1),
        Description("The index of the selected item.")
        ]
        public virtual int SelectedIndex {
            get {
                object o = ViewState["SelectedIndex"];
                if (o != null)
                    return(int)o;
                return -1;
            }
            set {
                if (value < -1) {
                    throw new ArgumentOutOfRangeException();
                }
                int oldSelectedIndex = SelectedIndex;
                ViewState["SelectedIndex"] = value;

                if (HasControls()) {
                    Table table = (Table)Controls[0];
                    TemplatedListItem item;

                    if ((oldSelectedIndex != -1) && (table.Rows.Count > oldSelectedIndex)) {
                        item = (TemplatedListItem)table.Rows[oldSelectedIndex];

                        if (item.ItemType != ListItemType.EditItem) {
                            ListItemType itemType = ListItemType.Item;
                            if (oldSelectedIndex % 2 != 0)
                                itemType = ListItemType.AlternatingItem;
                            item.SetItemType(itemType);
                        }
                    }
                    if ((value != -1) && (table.Rows.Count > value)) {
                        item = (TemplatedListItem)table.Rows[value];
                        item.SetItemType(ListItemType.SelectedItem);
                    }
                }
            }
        }

        [
        Category("Style"),
        Description("The style to be applied to the selected item."),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
        NotifyParentProperty(true),
        PersistenceMode(PersistenceMode.InnerProperty),
        ]
        public virtual TableItemStyle SelectedItemStyle {
            get {
                if (selectedItemStyle == null) {
                    selectedItemStyle = new TableItemStyle();
                    if (IsTrackingViewState)
                        ((IStateManager)selectedItemStyle).TrackViewState();
                }
                return selectedItemStyle;
            }
        }
#endregion

#region Events
        protected virtual void OnItemCommand(TemplatedListCommandEventArgs e) {
            TemplatedListCommandEventHandler onItemCommandHandler = (TemplatedListCommandEventHandler)Events[EventItemCommand];
            if (onItemCommandHandler != null) onItemCommandHandler(this, e);
        }

        protected virtual void OnItemCreated(TemplatedListItemEventArgs e) {
            TemplatedListItemEventHandler onItemCreatedHandler = (TemplatedListItemEventHandler)Events[EventItemCreated];
            if (onItemCreatedHandler != null) onItemCreatedHandler(this, e);
        }

        protected virtual void OnItemDataBound(TemplatedListItemEventArgs e) {
            TemplatedListItemEventHandler onItemDataBoundHandler = (TemplatedListItemEventHandler)Events[EventItemDataBound];
            if (onItemDataBoundHandler != null) onItemDataBoundHandler(this, e);
        }

        protected virtual void OnSelectedIndexChanged(EventArgs e) {
            EventHandler handler = (EventHandler)Events[EventSelectedIndexChanged];
            if (handler != null) handler(this, e);
        }

        [
        Category("Action"),
        Description("Raised when a CommandEvent occurs within an item.")
        ]
        public event TemplatedListCommandEventHandler ItemCommand {
            add {
                Events.AddHandler(EventItemCommand, value);
            }
            remove {
                Events.RemoveHandler(EventItemCommand, value);
            }
        }

        [
        Category("Behavior"),
        Description("Raised when an item is created and is ready for customization.")
        ]
        public event TemplatedListItemEventHandler ItemCreated {
            add {
                Events.AddHandler(EventItemCreated, value);
            }
            remove {
                Events.RemoveHandler(EventItemCreated, value);
            }
        }

        [
        Category("Behavior"),
        Description("Raised when an item is data-bound.")
        ]
        public event TemplatedListItemEventHandler ItemDataBound {
            add {
                Events.AddHandler(EventItemDataBound, value);
            }
            remove {
                Events.RemoveHandler(EventItemDataBound, value);
            }
        }

        [
        Category("Action"),
        Description("Raised when the SelectedIndex property has changed.")
        ]
        public event EventHandler SelectedIndexChanged {
            add {
                Events.AddHandler(EventSelectedIndexChanged, value);
            }
            remove {
                Events.RemoveHandler(EventSelectedIndexChanged, value);
            }
        }
#endregion

#region Methods and Implementation
        protected override void CreateChildControls() {
            Controls.Clear();

            if (ViewState["ItemCount"] != null) {
                // Create the control hierarchy using the view state, 
                // not the data source.
                CreateControlHierarchy(false);
            }
        }

        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) {
                Table table = new Table();
                Controls.Add(table);

                int selectedItemIndex = SelectedIndex;
                int index = 0;

                count = 0;
                foreach (object dataItem in dataSource) {
                    ListItemType itemType = ListItemType.Item;
                    if (index == selectedItemIndex) {
                        itemType = ListItemType.SelectedItem;
                    }
                    else if (index % 2 != 0) {
                        itemType = ListItemType.AlternatingItem;
                    }

                    CreateItem(table, index, itemType, useDataSource, dataItem);
                    count++;
                    index++;
                }
            }

            if (useDataSource) {
                // Save the number of items contained for use in round trips.
                ViewState["ItemCount"] = ((dataSource != null) ? count : -1);
            }
        }

        protected override Style CreateControlStyle() {
            // Since the TemplatedList control renders an HTML table, 
            // an instance of the TableStyle class is used as the control style.

            TableStyle style = new TableStyle(ViewState);

            // Set up default initial state.
            style.CellSpacing = 0;

            return style;
        }

        private TemplatedListItem CreateItem(Table table, int itemIndex, ListItemType itemType, bool dataBind, object dataItem) {
            TemplatedListItem item = new TemplatedListItem(itemIndex, itemType);
            TemplatedListItemEventArgs e = new TemplatedListItemEventArgs(item);

            if (itemTemplate != null) {
                itemTemplate.InstantiateIn(item.Cells[0]);
            }
            if (dataBind) {
                item.DataItem = dataItem;
            }
            OnItemCreated(e);
            table.Rows.Add(item);

            if (dataBind) {
                item.DataBind();
                OnItemDataBound(e);

                item.DataItem = null;
            }

            return item;
        }

        public override void DataBind() {
            // Controls with a data-source property perform their custom data binding
            // by overriding DataBind.

            // Evaluate any data-binding expressions on the control itself.
            base.OnDataBinding(EventArgs.Empty);

            // Reset the control state.
            Controls.Clear();
            ClearChildViewState();

            //  Create the control hierarchy using the data source.
            CreateControlHierarchy(true);
            ChildControlsCreated = true;

            TrackViewState();
        }

        protected override void LoadViewState(object savedState) {
            // Customize state management to handle saving state of contained objects.

            if (savedState != null) {
                object[] myState = (object[])savedState;

                if (myState[0] != null)
                    base.LoadViewState(myState[0]);
                if (myState[1] != null)
                    ((IStateManager)ItemStyle).LoadViewState(myState[1]);
                if (myState[2] != null)
                    ((IStateManager)SelectedItemStyle).LoadViewState(myState[2]);
                if (myState[3] != null)
                    ((IStateManager)AlternatingItemStyle).LoadViewState(myState[3]);
            }
        }

        protected override bool OnBubbleEvent(object source, EventArgs e) {
            // Handle events raised by children by overriding OnBubbleEvent.

            bool handled = false;

            if (e is TemplatedListCommandEventArgs) {
                TemplatedListCommandEventArgs ce = (TemplatedListCommandEventArgs)e;

                OnItemCommand(ce);
                handled = true;

                if (String.Compare(ce.CommandName, "Select", true) == 0) {
                    SelectedIndex = ce.Item.ItemIndex;
                    OnSelectedIndexChanged(EventArgs.Empty);
                }
            }

            return handled;
        }

        private void PrepareControlHierarchy() {
            if (HasControls() == false) {
                return;
            }

            Debug.Assert(Controls[0] is Table);
            Table table = (Table)Controls[0];

            table.CopyBaseAttributes(this);
            if (ControlStyleCreated) {
                table.ApplyStyle(ControlStyle);
            }

            // The composite alternating item style; do just one
            // merge style on the actual item.
            Style altItemStyle = null;
            if (alternatingItemStyle != null) {
                altItemStyle = new TableItemStyle();
                altItemStyle.CopyFrom(itemStyle);
                altItemStyle.CopyFrom(alternatingItemStyle);
            }
            else {
                altItemStyle = itemStyle;
            }

            int rowCount = table.Rows.Count;
            for (int i = 0; i < rowCount; i++) {
                TemplatedListItem item = (TemplatedListItem)table.Rows[i];
                Style compositeStyle = null;

                switch (item.ItemType) {
                    case ListItemType.Item:
                        compositeStyle = itemStyle;
                        break;

                    case ListItemType.AlternatingItem:
                        compositeStyle = altItemStyle;
                        break;

                    case ListItemType.SelectedItem: {
                        compositeStyle = new TableItemStyle();

                        if (item.ItemIndex % 2 != 0)
                            compositeStyle.CopyFrom(altItemStyle);
                        else
                            compositeStyle.CopyFrom(itemStyle);
                        compositeStyle.CopyFrom(selectedItemStyle);
                    }
                        break;
                }

                if (compositeStyle != null) {
                    item.MergeStyle(compositeStyle);
                }
            }
        }

        protected override void Render(HtmlTextWriter writer) {
            // Apply styles to the control hierarchy
            // and then render it out.

            // Apply styles during render phase, so the user can change styles
            // after calling DataBind without the property changes ending
            // up in view state.
            PrepareControlHierarchy();

            RenderContents(writer);
        }

        protected override object SaveViewState() {
            // Customized state management to handle saving state of contained objects such as styles.

            object baseState = base.SaveViewState();
            object itemStyleState = (itemStyle != null) ? ((IStateManager)itemStyle).SaveViewState() : null;
            object selectedItemStyleState = (selectedItemStyle != null) ? ((IStateManager)selectedItemStyle).SaveViewState() : null;
            object alternatingItemStyleState = (alternatingItemStyle != null) ? ((IStateManager)alternatingItemStyle).SaveViewState() : null;

            object[] myState = new object[4];
            myState[0] = baseState;
            myState[1] = itemStyleState;
            myState[2] = selectedItemStyleState;
            myState[3] = alternatingItemStyleState;

            return myState;
        }

        protected override void TrackViewState() {
            // Customized state management to handle saving state of contained objects such as styles.

            base.TrackViewState();

            if (itemStyle != null)
                ((IStateManager)itemStyle).TrackViewState();
            if (selectedItemStyle != null)
                ((IStateManager)selectedItemStyle).TrackViewState();
            if (alternatingItemStyle != null)
                ((IStateManager)alternatingItemStyle).TrackViewState();
        }
#endregion
    }


    public class TemplatedListItem : TableRow, INamingContainer {
        private int itemIndex;
        private ListItemType itemType;
        private object dataItem;

        public TemplatedListItem(int itemIndex, ListItemType itemType) {
            this.itemIndex = itemIndex;
            this.itemType = itemType;

            Cells.Add(new TableCell());
        }

        public virtual object DataItem {
            get {
                return dataItem;
            }
            set {
                dataItem = value;
            }
        }

        public virtual int ItemIndex {
            get {
                return itemIndex;
            }
        }

        public virtual ListItemType ItemType {
            get {
                return itemType;
            }
        }

        protected override bool OnBubbleEvent(object source, EventArgs e) {
            if (e is CommandEventArgs) {
                // Add the information about Item to CommandEvent.

                TemplatedListCommandEventArgs args =
                    new TemplatedListCommandEventArgs(this, source, (CommandEventArgs)e);

                RaiseBubbleEvent(this, args);
                return true;
            }
            return false;
        }

        internal void SetItemType(ListItemType itemType) {
            this.itemType = itemType;
        }
    }

    public sealed class TemplatedListCommandEventArgs : CommandEventArgs {

        private TemplatedListItem item;
        private object commandSource;

        public TemplatedListCommandEventArgs(TemplatedListItem item, object commandSource, CommandEventArgs originalArgs) :
            base(originalArgs) {
            this.item = item;
            this.commandSource = commandSource;
        }

        public TemplatedListItem Item {
            get {
                return item;
            }
        }

        public object CommandSource {
            get {
                return commandSource;
            }
        }
    }

    public delegate void TemplatedListCommandEventHandler(object source, TemplatedListCommandEventArgs e);

    public sealed class TemplatedListItemEventArgs : EventArgs {

        private TemplatedListItem item;

        public TemplatedListItemEventArgs(TemplatedListItem item) {
            this.item = item;
        }

        public TemplatedListItem Item {
            get {
                return item;
            }
        }
    }

    public delegate void TemplatedListItemEventHandler(object sender, TemplatedListItemEventArgs e);

    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 DummyDataSourceEnumerator(dataItemCount);
        }


        private class DummyDataSourceEnumerator : IEnumerator {

            private int count;
            private int index;

            public DummyDataSourceEnumerator(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;
            }
        }
    }
}
[Visual Basic]
' TemplatedList.vb

Imports System
Imports System.ComponentModel
Imports System.ComponentModel.Design
Imports System.ComponentModel.Design.Serialization
Imports System.Collections
Imports System.Diagnostics
Imports System.Web.UI
Imports System.Web.UI.WebControls

Namespace CustomControls
   <DefaultEvent("SelectedIndexChanged"), _ 
      DefaultProperty("DataSource"), _
      Designer("CustomControls.Design.TemplatedListDesigner, CustomControls.Design", _
      GetType(IDesigner))> _
   Public Class TemplatedList
      Inherits WebControl
      Implements INamingContainer
      
#Region "Statics and Constants"
      Private Shared EventSelectedIndexChanged As New Object()
      Private Shared EventItemCreated As New Object()
      Private Shared EventItemDataBound As New Object()
      Private Shared EventItemCommand As New Object()
#End Region

#Region "Member variables"
      Private _dataSource As IEnumerable
      Private _itemStyle As TableItemStyle '
      Private _alternatingItemStyle As TableItemStyle
      Private _selectedItemStyle As TableItemStyle
      Private _itemTemplate As ITemplate
#End Region

#Region "Properties"
      <Category("Style"), _
         Description("The style to be applied to alternate items."), _
         DesignerSerializationVisibility(DesignerSerializationVisibility.Content), _
         NotifyParentProperty(True), _
         PersistenceMode(PersistenceMode.InnerProperty)> _
      Public Overridable ReadOnly Property AlternatingItemStyle() As TableItemStyle
         Get
            If _alternatingItemStyle Is Nothing Then
               _alternatingItemStyle = New TableItemStyle()
               If IsTrackingViewState Then
                  CType(_alternatingItemStyle, IStateManager).TrackViewState()
               End If
            End If
            Return _alternatingItemStyle
         End Get
      End Property

      <Bindable(True), _
         Category("Appearance"), _
         DefaultValue(- 1), _
         Description("The cell padding of the rendered table.")> _
      Public Overridable Property CellPadding() As Integer
         Get
            If ControlStyleCreated = False Then
               Return - 1
            End If
            Return CType(ControlStyle, TableStyle).CellPadding
         End Get
         Set
            CType(ControlStyle, TableStyle).CellPadding = value
         End Set
      End Property

      <Bindable(True), _
         Category("Appearance"), _
         DefaultValue(0), _
         Description("The cell spacing of the rendered table.")> _
      Public Overridable Property CellSpacing() As Integer
         Get
            If ControlStyleCreated = False Then
               Return 0
            End If
            Return CType(ControlStyle, TableStyle).CellSpacing
         End Get
         Set
            CType(ControlStyle, TableStyle).CellSpacing = value
         End Set
      End Property

      <Bindable(True), _
         Category("Data"), _
         Description("The data source used to build up the control."), _
         DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
      Public Property DataSource() As IEnumerable
         Get
            Return _dataSource
         End Get
         Set
            _dataSource = value
         End Set
      End Property

      <Bindable(True), _
         Category("Appearance"), _
         DefaultValue(System.Web.UI.WebControls.GridLines.None), _
         Description("The grid lines to be shown in the rendered table.")> _
      Public Overridable Property GridLines() As GridLines
         Get
            If ControlStyleCreated = False Then
               Return GridLines.None
            End If
            Return CType(ControlStyle, TableStyle).GridLines
         End Get
         Set
            CType(ControlStyle, TableStyle).GridLines = value
         End Set
      End Property

      <Category("Style"), _
         Description("The style to be applied to all items."), _
         DesignerSerializationVisibility(DesignerSerializationVisibility.Content), _
         NotifyParentProperty(True), _
         PersistenceMode(PersistenceMode.InnerProperty)> _
      Public Overridable ReadOnly Property ItemStyle() As TableItemStyle
         Get
            If _itemStyle Is Nothing Then
               _itemStyle = New TableItemStyle()
               If IsTrackingViewState Then
                  CType(_itemStyle, IStateManager).TrackViewState()
               End If
            End If
            Return _itemStyle
         End Get
      End Property

      <Browsable(False), _
         Description("The content to be shown in each item."), _
         PersistenceMode(PersistenceMode.InnerProperty), _
         TemplateContainer(GetType(TemplatedListItem))> _
      Public Overridable Property ItemTemplate() As ITemplate
         Get
            Return _itemTemplate
         End Get
         Set
            _itemTemplate = value
         End Set
      End Property

      <Bindable(True), _
         DefaultValue(- 1), _
         Description("The index of the selected item.")> _
      Public Overridable Property SelectedIndex() As Integer
         Get
            Dim o As Object = ViewState("SelectedIndex")
            If Not (o Is Nothing) Then
               Return CInt(o)
            End If
            Return - 1
         End Get
         Set
            If value < - 1 Then
               Throw New ArgumentOutOfRangeException()
            End If
            Dim oldSelectedIndex As Integer = SelectedIndex
            ViewState("SelectedIndex") = value
            
            If HasControls() Then
               Dim table As Table = CType(Controls(0), Table)
               Dim item As TemplatedListItem
               
               If oldSelectedIndex <> - 1 And table.Rows.Count > oldSelectedIndex Then
                  item = CType(table.Rows(oldSelectedIndex), TemplatedListItem)
                  
                  If item.ItemType <> ListItemType.EditItem Then
                     Dim itemType As ListItemType = ListItemType.Item
                     If oldSelectedIndex Mod 2 <> 0 Then
                        itemType = ListItemType.AlternatingItem
                     End If
                     item.SetItemType(itemType)
                  End If
               End If
               If value <> - 1 And table.Rows.Count > value Then
                  item = CType(table.Rows(value), TemplatedListItem)
                  item.SetItemType(ListItemType.SelectedItem)
               End If
            End If
         End Set
      End Property

      <Category("Style"), _
         Description("The style to be applied to the selected item."), _
         DesignerSerializationVisibility( _
         DesignerSerializationVisibility.Content), _
         NotifyParentProperty(True), _
         PersistenceMode(PersistenceMode.InnerProperty)> _
      Public Overridable ReadOnly Property SelectedItemStyle() As TableItemStyle
         Get
            If _selectedItemStyle Is Nothing Then
               _selectedItemStyle = New TableItemStyle()
               If IsTrackingViewState Then
                  CType(_selectedItemStyle, IStateManager).TrackViewState()
               End If
            End If
            Return _selectedItemStyle
         End Get
      End Property
#End Region
      
#Region "Events"
      Protected Overridable Sub OnItemCommand(e As TemplatedListCommandEventArgs)
         Dim onItemCommandHandler As TemplatedListCommandEventHandler = CType(Events(EventItemCommand), TemplatedListCommandEventHandler)
         If Not (onItemCommandHandler Is Nothing) Then
            onItemCommandHandler(Me, e)
         End If
      End Sub
       
      Protected Overridable Sub OnItemCreated(e As TemplatedListItemEventArgs)
         Dim onItemCreatedHandler As TemplatedListItemEventHandler = CType(Events(EventItemCreated), TemplatedListItemEventHandler)
         If Not (onItemCreatedHandler Is Nothing) Then
            onItemCreatedHandler(Me, e)
         End If
      End Sub
       
      Protected Overridable Sub OnItemDataBound(e As TemplatedListItemEventArgs)
         Dim onItemDataBoundHandler As TemplatedListItemEventHandler = CType(Events(EventItemDataBound), TemplatedListItemEventHandler)
         If Not (onItemDataBoundHandler Is Nothing) Then
            onItemDataBoundHandler(Me, e)
         End If
      End Sub
       
      Protected Overridable Sub OnSelectedIndexChanged(e As EventArgs)
         Dim handler As EventHandler = CType(Events(EventSelectedIndexChanged), EventHandler)
         If Not (handler Is Nothing) Then
            handler(Me, e)
         End If
      End Sub
      
      <Category("Action"), _
         Description("Raised when a CommandEvent occurs within an item.")> _
      Public Event ItemCommand As TemplatedListCommandEventHandler
      
      <Category("Behavior"), _
         Description("Raised when an item is created and is ready for customization.")> _
      Public Event ItemCreated As TemplatedListItemEventHandler
      
      <Category("Behavior"), _
         Description("Raised when an item is data-bound.")> _
      Public Event ItemDataBound As TemplatedListItemEventHandler
      
      <Category("Action"), _
         Description("Raised when the SelectedIndex property has changed.")> _
      Public Event SelectedIndexChanged As EventHandler
#End Region

#Region "Methods and Implementation"
      Protected Overrides Sub CreateChildControls()
         Controls.Clear() 
         If Not (ViewState("ItemCount") Is Nothing) Then
            ' Create the control hierarchy using the view state, 
            ' not the data source.
            CreateControlHierarchy(False)
         End If
      End Sub
      
      Private Sub CreateControlHierarchy(useDataSource As Boolean)
         Dim dataSource As IEnumerable = Nothing
         Dim count As Integer = - 1
         
         If useDataSource = False Then
            ' ViewState must have a non-null value for ItemCount because this is checked 
            ' in CreateChildControls.
            count = CInt(ViewState("ItemCount"))
            If count <> - 1 Then
               dataSource = New DummyDataSource(count)
            End If
         Else
            dataSource = Me._dataSource
         End If
         
         If Not (dataSource Is Nothing) Then
            Dim table As New Table()
            Controls.Add(table)
            
            Dim selectedItemIndex As Integer = SelectedIndex
            Dim index As Integer = 0
            
            count = 0
            Dim dataItem As Object
            For Each dataItem In  dataSource
               Dim itemType As ListItemType = ListItemType.Item
               If index = selectedItemIndex Then
                  itemType = ListItemType.SelectedItem
               Else
                  If index Mod 2 <> 0 Then
                     itemType = ListItemType.AlternatingItem
                  End If
               End If 
               CreateItem(table, index, itemType, useDataSource, dataItem)
               count += 1
               index += 1
            Next dataItem
         End If
         
         If useDataSource Then
            ' Save the number of items contained for use in round trips.
            If Not (dataSource Is Nothing) Then
               ViewState("ItemCount") = count
            Else
               ViewState("ItemCount") = -1
            End If
         End If
      End Sub
      
      
      Protected Overrides Function CreateControlStyle() As Style
         ' Since the TemplatedList control renders an HTML table, 
         ' an instance of the TableStyle class is used as the control style.
         Dim style As New TableStyle(ViewState)
         ' Set up default initial state.
         style.CellSpacing = 0
         Return style
      End Function
      
      Private Function CreateItem(table As Table, itemIndex As Integer, itemType As ListItemType, dataBind As Boolean, dataItem As Object) As TemplatedListItem
         Dim item As New TemplatedListItem(itemIndex, itemType)
         Dim e As New TemplatedListItemEventArgs(item)
         
         If Not (_itemTemplate Is Nothing) Then
            _itemTemplate.InstantiateIn(item.Cells(0))
         End If
         If dataBind Then
            item.DataItem = dataItem
         End If
         OnItemCreated(e)
         table.Rows.Add(item)
         
         If dataBind Then
            item.DataBind()
            OnItemDataBound(e)
            
            item.DataItem = Nothing
         End If
         
         Return item
      End Function
      
      Public Overrides Sub DataBind()
         ' Controls with a data-source property perform their custom data binding
         ' by overriding DataBind.
         ' Evaluate any data-binding expressions on the control itself.
         MyBase.OnDataBinding(EventArgs.Empty)
         
         ' Reset the control state.
         Controls.Clear()
         ClearChildViewState()
         
         '  Create the control hierarchy using the data source.
         CreateControlHierarchy(True)
         ChildControlsCreated = True
         
         TrackViewState()
      End Sub
      
      Protected Overrides Sub LoadViewState(savedState As Object)
         ' Customized state management to handle saving state of contained objects.
         If Not (savedState Is Nothing) Then
            Dim myState As Object() = CType(savedState, Object())
            
            If Not (myState(0) Is Nothing) Then
               MyBase.LoadViewState(myState(0))
            End If
            If Not (myState(1) Is Nothing) Then
               CType(ItemStyle, IStateManager).LoadViewState(myState(1))
            End If
            If Not (myState(2) Is Nothing) Then
               CType(SelectedItemStyle, IStateManager).LoadViewState(myState(2))
            End If
            If Not (myState(3) Is Nothing) Then
               CType(AlternatingItemStyle, IStateManager).LoadViewState(myState(3))
            End If
         End If
      End Sub
       
      Protected Overrides Function OnBubbleEvent(source As Object, e As EventArgs) As Boolean
         ' Handle events raised by children by overriding OnBubbleEvent.
         Dim handled As Boolean = False
         
         If TypeOf e Is TemplatedListCommandEventArgs Then
            Dim ce As TemplatedListCommandEventArgs = CType(e, TemplatedListCommandEventArgs)
            
            OnItemCommand(ce)
            handled = True
            
            If String.Compare(ce.CommandName, "Select", True) = 0 Then
               SelectedIndex = ce.Item.ItemIndex
               OnSelectedIndexChanged(EventArgs.Empty)
            End If
         End If
         
         Return handled
      End Function
      
      Private Sub PrepareControlHierarchy()
         If HasControls() = False Then
            Return
         End If
         
         Debug.Assert(TypeOf Controls(0) Is Table)
         Dim table As Table = CType(Controls(0), Table)
         
         table.CopyBaseAttributes(Me)
         If ControlStyleCreated Then
            table.ApplyStyle(ControlStyle)
         End If
         
         ' The composite alternating item style; do just one
         ' merge style on the actual item.
         Dim altItemStyle As Style = Nothing
         If Not (_alternatingItemStyle Is Nothing) Then
            altItemStyle = New TableItemStyle()
            altItemStyle.CopyFrom(_itemStyle)
            altItemStyle.CopyFrom(_alternatingItemStyle)
         Else
            altItemStyle = _itemStyle
         End If
         
         Dim rowCount As Integer = table.Rows.Count
         Dim i As Integer
         For i = 0 To rowCount - 1
            Dim item As TemplatedListItem = CType(table.Rows(i), TemplatedListItem)
            Dim compositeStyle As Style = Nothing
            
            Select Case item.ItemType
               Case ListItemType.Item
                  compositeStyle = _itemStyle
               
               Case ListItemType.AlternatingItem
                  compositeStyle = altItemStyle
               
               Case ListItemType.SelectedItem
                  If (True) Then
                     compositeStyle = New TableItemStyle()
                     
                     If item.ItemIndex Mod 2 <> 0 Then
                        compositeStyle.CopyFrom(altItemStyle)
                     Else
                        compositeStyle.CopyFrom(_itemStyle)
                     End If
                     compositeStyle.CopyFrom(_selectedItemStyle)
                  End If
            End Select
            
            If Not (compositeStyle Is Nothing) Then
               item.MergeStyle(compositeStyle)
            End If
         Next i
      End Sub
      
      Protected Overrides Sub Render(writer As HtmlTextWriter)
         ' Apply styles to the control hierarchy
         ' and then render it out.
         ' Apply styles during render phase, so the user can change styles
         ' after calling DataBind without the property changes ending
         ' up in view state.
         PrepareControlHierarchy()
         
         RenderContents(writer)
      End Sub
      
      Protected Overrides Function SaveViewState() As Object
         ' Customize state management to handle saving state of contained objects such as styles.
         Dim baseState As Object = MyBase.SaveViewState()
         Dim itemStyleState As Object 
         Dim selectedItemStyleState As Object
         Dim alternatingItemStyleState As Object

         If Not (_itemStyle Is Nothing) Then 
            itemStyleState = CType(_itemStyle, IStateManager).SaveViewState()
         Else
            itemStyleState = Nothing
         End If

         If Not (_selectedItemStyle Is Nothing) Then
            selectedItemStyleState = CType(_selectedItemStyle, IStateManager).SaveViewState()
         Else
            selectedItemStyleState = Nothing
         End If

         If Not (_alternatingItemStyle Is Nothing) Then
            alternatingItemStyleState = CType(_alternatingItemStyle, IStateManager).SaveViewState()
         Else
            alternatingItemStyleState = Nothing
         End If
        
         Dim myState(4) As Object
         myState(0) = baseState
         myState(1) = itemStyleState
         myState(2) = selectedItemStyleState
         myState(3) = alternatingItemStyleState
         
         Return myState
      End Function
      
      Protected Overrides Sub TrackViewState()
         ' Customize state management to handle saving state of contained objects such as styles.
         MyBase.TrackViewState()
         
         If Not (_itemStyle Is Nothing) Then
            CType(_itemStyle, IStateManager).TrackViewState()
         End If
         If Not (_selectedItemStyle Is Nothing) Then
            CType(_selectedItemStyle, IStateManager).TrackViewState()
         End If
         If Not (_alternatingItemStyle Is Nothing) Then
            CType(_alternatingItemStyle, IStateManager).TrackViewState()
         End If
      End Sub
 #End Region
   End Class
   
   Public Class TemplatedListItem
      Inherits TableRow
      Implements INamingContainer
      Private _itemIndex As Integer
      Private _itemType As ListItemType
      Private _dataItem As Object
      
      Public Sub New(itemIndex As Integer, itemType As ListItemType)
         Me._itemIndex = itemIndex
         Me._itemType = itemType
         
         Cells.Add(New TableCell())
      End Sub
      
      Public Overridable Property DataItem() As Object
         Get
            Return _dataItem
         End Get
         Set
            _dataItem = value
         End Set
      End Property
      
      Public Overridable ReadOnly Property ItemIndex() As Integer
         Get
            Return _itemIndex
         End Get
      End Property
      
      Public Overridable ReadOnly Property ItemType() As ListItemType
         Get
            Return _itemType
         End Get
      End Property
      
      Protected Overrides Function OnBubbleEvent(source As Object, e As EventArgs) As Boolean
         If TypeOf e Is CommandEventArgs Then
            ' Add the information about Item to CommandEvent.
            Dim args As New TemplatedListCommandEventArgs(Me, source, CType(e, CommandEventArgs))
            
            RaiseBubbleEvent(Me, args)
            Return True
         End If
         Return False
      End Function
      
      Friend Sub SetItemType(itemType As ListItemType)
         Me._itemType = itemType
      End Sub
   End Class
   
   NotInheritable Public Class TemplatedListCommandEventArgs
      Inherits CommandEventArgs
      
      Private _item As TemplatedListItem
      Private _commandSource As Object
      
      Public Sub New(item As TemplatedListItem, commandSource As Object, originalArgs As CommandEventArgs)
         MyBase.New(originalArgs)
         Me._item = item
         Me._commandSource = commandSource
      End Sub
      
      Public ReadOnly Property Item() As TemplatedListItem
         Get
            Return _item
         End Get
      End Property
      
      
      Public ReadOnly Property CommandSource() As Object
         Get
            Return _commandSource
         End Get
      End Property
   End Class
   
   Public Delegate Sub TemplatedListCommandEventHandler(source As Object, e As TemplatedListCommandEventArgs)
   
   NotInheritable Public Class TemplatedListItemEventArgs
      Inherits EventArgs
      
      Private _item As TemplatedListItem
      
      Public Sub New(item As TemplatedListItem)
         Me._item = item
      End Sub
      
      Public ReadOnly Property Item() As TemplatedListItem
         Get
            Return _item
         End Get
      End Property
   End Class
   
   Public Delegate Sub TemplatedListItemEventHandler(sender As Object, e As TemplatedListItemEventArgs)
   
   NotInheritable Friend Class DummyDataSource
      Implements ICollection
      
      Private dataItemCount As Integer
      
      Public Sub New(dataItemCount As Integer)
         Me.dataItemCount = dataItemCount
      End Sub
      
      Public ReadOnly Property Count() As Integer Implements ICollection.Count
         Get
            Return dataItemCount
         End Get
      End Property
      
      Public ReadOnly Property IsReadOnly() As Boolean
         Get
            Return False
         End Get
      End Property
      
      Public ReadOnly Property IsSynchronized() As Boolean Implements ICollection.IsSynchronized
         Get
            Return False
         End Get
      End Property
      
      Public ReadOnly Property SyncRoot() As Object Implements ICollection.SyncRoot
         Get
            Return Me
         End Get
      End Property
      
      Public Sub CopyTo(array As Array, index As Integer) Implements ICollection.CopyTo
         Dim e As IEnumerator
         
         While e.MoveNext()
            array.SetValue(e.Current, index)
            index += 1
         End While
      End Sub
       
      Public Function GetEnumerator() As IEnumerator Implements ICollection.GetEnumerator
         Return New DummyDataSourceEnumerator(dataItemCount)
      End Function
      
      Private Class DummyDataSourceEnumerator
         Implements IEnumerator
         
         Private count As Integer
         Private index As Integer
         
         Public Sub New(count As Integer)
            Me.count = count
            Me.index = - 1
         End Sub
         
         Public ReadOnly Property Current() As Object Implements IEnumerator.Current
            Get
               Return Nothing
            End Get
         End Property
         
         Public Function MoveNext() As Boolean Implements IEnumerator.MoveNext
            index += 1
            Return index < count
         End Function
         
         Public Sub Reset() Implements IEnumerator.Reset
            Me.index = - 1
         End Sub
      End Class
   End Class
End Namespace

Using the Data-Bound Control on a Page

The following sample shows an ASP.NET page that uses the TemplatedList control.

<%@ Page language="C#" %>
<%@ Register TagPrefix="custom" Namespace="CustomControls" Assembly="CustomControls"%>
<html>
<head>
  <title>Data-Bound Control Sample</title>
</head>
<body>
<form runat="server">
<h3>TemplatedList Control Sample</h3>
<hr>
<custom:TemplatedList runat="server" id="myList"
    Font-Name="Verdana" Font-Size="16pt"
    BorderColor="Gray" BorderWidth="1px"
    CellSpacing="0" CellPadding="2" GridLines="Both"
    onItemCreated="MyList_ItemCreated"
    onSelectedIndexChanged="MyList_SelectedIndexChanged">
  <ItemStyle ForeColor="Black" BackColor="#EEEEEE"/>
  <AlternatingItemStyle BackColor="#DCDCDC"/>
  <SelectedItemStyle ForeColor="White" BackColor="#000084"/>
  
  <ItemTemplate>
    <asp:Button runat="server" id="selectButton" CommandName="Select"
        Text="Select" ForeColor="Blue"></asp:Button>&nbsp;&nbsp;
    <asp:Label runat="server" Text='<%# Container.DataItem %>'/>
  </ItemTemplate>
</custom:TemplatedList>
<hr>
<asp:Label runat="server" id="infoLabel"></asp:Label>
<hr>
</form>
<script runat="server">

private int selectedIndex = -1;

private void LoadData() {
    ArrayList data = new ArrayList();

    for (int i = 0; i < 10; i++) {
        data.Add("Item " + i);
    }

    myList.DataSource = data;
    myList.DataBind();
}

protected override void OnLoad(EventArgs e) {
    base.OnLoad(e);
    if (!IsPostBack) {
        LoadData();
    }
}

protected void MyList_ItemCreated(object sender, TemplatedListItemEventArgs e) {
    if (e.Item.ItemType == ListItemType.SelectedItem) {
        selectedIndex = e.Item.ItemIndex;

        Button selectButton = (Button)e.Item.FindControl("selectButton");
        selectButton.Enabled = false;
    }
}

protected void MyList_SelectedIndexChanged(object sender, EventArgs e) {
    if (selectedIndex != -1) {
        Control item = myList.Controls[0].Controls[selectedIndex];
        Button selectButton = (Button)item.FindControl("selectButton");
        selectButton.Enabled = true;
    }

    selectedIndex = myList.SelectedIndex;
    infoLabel.Text = "SelectedIndex: " + selectedIndex;

    if (selectedIndex != -1) {
        Control item = myList.Controls[0].Controls[selectedIndex];
        Button selectButton = (Button)item.FindControl("selectButton");
        selectButton.Enabled = false;
    }
}
</script>
</body>
</html>
[Visual Basic]
<%@ Page language="VB"%>
<%@ Register TagPrefix="custom" Namespace="CustomControls" Assembly="CustomControls"%>
<html>
<head>
  <title>Data-Bound Control Sample</title>
</head>
<body>
<form runat="server">
<h3>TemplatedList Control Sample</h3>
<hr>
<custom:TemplatedList runat="server" id="myList"
    Font-Name="Verdana" Font-Size="16pt"
    BorderColor="Gray" BorderWidth="1px"
    CellSpacing="0" CellPadding="2" GridLines="Both"
    onItemCreated="MyList_ItemCreated"
    onSelectedIndexChanged="MyList_SelectedIndexChanged">
  <ItemStyle ForeColor="Black" BackColor="#EEEEEE"/>
  <AlternatingItemStyle BackColor="#DCDCDC"/>
  <SelectedItemStyle ForeColor="White" BackColor="#000084"/>
  
  <ItemTemplate>
    <asp:Button runat="server" id="selectButton" CommandName="Select"
        Text="Select" ForeColor="Blue"></asp:Button>&nbsp;&nbsp;
    <asp:Label runat="server" Text='<%# Container.DataItem %>'/>
  </ItemTemplate>
</custom:TemplatedList>
<hr>
<asp:Label runat="server" id="infoLabel"></asp:Label>
<hr>
</form>
<script runat="server">
   Private selectedIndex As Integer = - 1

   Private Sub LoadData()
      Dim data As New ArrayList()
      
      Dim i As Integer
      For i = 0 To 9
         data.Add("Item " & i)
      Next i
      
      myList.DataSource = data
      myList.DataBind()
   End Sub

   Protected Overrides Sub OnLoad(e As EventArgs)
      MyBase.OnLoad(e)
      If Not IsPostBack Then
         LoadData()
      End If
   End Sub

   Protected Sub MyList_ItemCreated(sender As Object, e As TemplatedListItemEventArgs)
      If e.Item.ItemType = ListItemType.SelectedItem Then
         selectedIndex = e.Item.ItemIndex
         
         Dim selectButton As Button = CType(e.Item.FindControl("selectButton"), Button)
         selectButton.Enabled = False
      End If
   End Sub

   Protected Sub MyList_SelectedIndexChanged(sender As Object, e As EventArgs)
      If selectedIndex <> - 1 Then
         Dim item As Control = myList.Controls(0).Controls(selectedIndex)
         Dim selectButton As Button = CType(item.FindControl("selectButton"), Button)
         selectButton.Enabled = True
      End If
      
      selectedIndex = myList.SelectedIndex
      infoLabel.Text = "SelectedIndex: " + selectedIndex
      
      If selectedIndex <> - 1 Then
         Dim item As Control = myList.Controls(0).Controls(selectedIndex)
         Dim selectButton As Button = CType(item.FindControl("selectButton"), Button)
         selectButton.Enabled = False
      End If
   End Sub
</script>
</body>
</html>

See Also

Developing a Templated Data-Bound Control

Show:
© 2014 Microsoft