模板化数据绑定控件示例

在以下示例中描述的 TemplatedList 控件是 System.Web.UI.DataList 控件的简化版本。该控件阐释以下概念。
  • 数据绑定,在开发模板化数据绑定控件中描述。

    DataSourceTemplatedList 的数据源。

    ItemTemplateTemplatedList 的模板属性。

    TemplatedListItemItemTemplate 的逻辑容器。TemplatedListItem 具有属性 DataItem,该属性绑定到 DataSource 的每一元素。

  • 事件冒泡,在事件冒泡中描述。

    TemplatedListItem 将命令事件从模板中的控件冒泡到 TemplatedListTemplatedList 将冒泡事件作为 ItemCommand 事件公开。TemplatedListCommandEventArgs 类包含该事件的数据。

  • 公开来自服务器控件中的样式中所描述控件的样式。

    TemplatedList 公开类型 System.Web.UI.WebControls.TableStyleItemStyleAlternatingItemStyleSelectedItemStyle 属性。

  • 使用 ViewState 自定义状态还原。

    TemplatedList 重写从 Control 继承的 SaveViewStateLoadViewState 方法以自定义其 Style 属性的还原。

  • 为设计时支持应用元数据属性。有关设计时属性的信息,请参见属性与设计时支持。在 DesignerAttribute 中指定的设计器 (CustomControls.Design.TemplatedListDesigner) 是在 Web 窗体模板数据绑定控件设计器示例中实现的。

若要生成该示例,请参见服务器控件示例中的说明。

[C#]
// 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;
            }
        }
    }
}





在页上使用数据绑定控件

以下示例说明使用 TemplatedList 控件的 ASP.NET 页。

[C#]
<%@ 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>

posted on 2009-01-19 17:24  jdmei520  阅读(312)  评论(0编辑  收藏  举报

导航