zoukankan      html  css  js  c++  java
  • 一起谈.NET技术,asp.net控件开发基础(17) 狼人:

      本篇将开始介绍如自定义数据绑定控件,这里感谢很多人的支持,有你们的支持很高兴。这里首先需要大家熟悉asp.net模板控件的使用,还有自定义模板控件.因为数据绑定控件多是基于模板控件的.

      一.回顾

      如果你使用过asp.net内置的数据控件(如DataList,Repeater),你一定会这么做

      1.设置数据源 DataSource属性

      2.调用数据绑定  DataBind方法

      3.在控件的不同模板内使用绑定语法显示数据

      这三步应该是必须要做的

      其他更多的

      你可能需要对绑定的数据进行统一的一些操作(如时间格式化),或者对数据的某一项进行操作(对某一项进行格式化),或者需要触发模板控件内的一些事件(如databound事件)。

      根据上面的一些需求,我们需要这样做

      1.对绑定的数据进行统一的一些操作: 为数据绑定控件定义Item项(表示列表的一条数据, 如Repeater的RepeaterItem)

      2.对数据的某一项进行操作: 因为定义了Item项,那你肯定需要一个ItemCollection集合,其可以方便的为你检索数据

      3.因为定义了RepeaterItem,原先的EventArgs和CommandEventArgs已经无法满足需求,我们需要自定义委托及其一个为控件提供数据的的ItemEventArgs

      上面三点有些并非必须定义,如第2点,还需要根据具体需求来定.但一个完成的控件是需要的。

      二.为数据控件做好准备

      这次的demo为不完整的Datalist控件,来源还是MSDN的例子,我们命名为TemplatedList,此控件未定义ItemCollection集合,好了,根据上面的分析我们先为TemplatedList提供项和委托及为事件提供数据的几个EventArgs,请看下面类图

      1.TemplatedListCommandEventArgs为Command事件提供数据

      2.TemplatedListItemEventArgs为一般项提供数据

      3.TemplatedListItem表示TemplatedList的项

      三.编写TemplatedList

      1.TemplatedList主要功能简介

      提供一个ItemTemplate模板属性,提供三种不同项样式,ItemCommand 事件冒泡事件及4个事件

      2.实现主要步骤

      以下为必须

      (1)控件必须实现 System.Web.UI.INamingContainer 接口

      (2)定义至少一个模板属性

      (3)定义DataSource数据源属性

      (4)定义控件项DataItem,即模板的一个容器

      (5)重写DataBind 方法及复合控件相关方法(模板控件为特殊的复合控件)

      当然还有其他额外的属性,样式,事件

      3.具体实现

      下面我们来具体看实现方法

      (1)定义控件成员属性

    #region 静态变量

    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 成员变量
    private IEnumerable dataSource;
    private TableItemStyle itemStyle;
    private TableItemStyle alternatingItemStyle;
    private TableItemStyle selectedItemStyle;
    private ITemplate itemTemplate;
    #endregion

    #region 控件属性

    [
    Category(
    "Style"),
    Description(
    "交替项样式"),
    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;
    }
    }


    [
    Category(
    "Style"),
    Description(
    "一般项样式"),
    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;
    }
    }

    [
    Category(
    "Style"),
    Description(
    "选中项样式"),
    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;
    }
    }



    [
    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(
    "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;
    }
    }

    [
    Bindable(
    true),
    Category(
    "Data"),
    DefaultValue(
    null),
    Description(
    "数据源"),
    DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
    ]
    public IEnumerable DataSource
    {
    get
    {
    return dataSource;
    }
    set
    {
    dataSource
    = value;
    }
    }


    [
    Browsable(
    false),
    DefaultValue(
    null),
    Description(
    "项模板"),
    PersistenceMode(PersistenceMode.InnerProperty),
    TemplateContainer(
    typeof(TemplatedListItem))
    ]
    public virtual ITemplate ItemTemplate
    {
    get
    {
    return itemTemplate;
    }
    set
    {
    itemTemplate
    = value;
    }
    }


    [
    Bindable(
    true),
    DefaultValue(
    -1),
    Description(
    "选中项索引,默认为-1")
    ]
    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);
    }
    }
    }
    }


    #endregion
    成员如下(可以看上面类图)

      1.三个项样式和三个样式属性

      2.公开DataSource数据源属性,一个模板属性

      3.SelectedIndex索引属性

      前面的相信大家都很容易明白,其中的三个项样式我们需要为其重写视图状态管理,不熟悉可以看以前的随笔,这里不再重复。SelectedIndex属性比较复杂,这里重点介绍此属性

      SelectedIndex索引属性默认为-1,我给出了注释,在赋值前先记录下了上次的选中项,为恢复样式而做准备 

                    //获取上次选中项
                    int oldSelectedIndex = SelectedIndex;
                    ViewState[
    "SelectedIndex"= value;

    当第一次更改SelectedIndex属性时只执行下列代码(将此项标记为选中项),因为初始化时的没有oldSelectedIndex,不需要恢复样式
    //第一次执行此项,并一直执行
                        if ((value != -1&& (table.Rows.Count > value))
                        
    {
                            item 
    = (TemplatedListItem)table.Rows[value];
                            item.SetItemType(ListItemType.SelectedItem);
                        }
    再次执行时,恢复oldSelectedIndex选中项样式

    //第一次选中项不执行
    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);
    }
    }

    相信这样的解释你会明白

      (2)定义控件成员事件

      我们可以用上刚才我们声明的委托了,即然你定义了这么多事件,就该为其安排触发的先后.所以这个要特别注意,等下会再次提到.

    #region 事件
    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

      (3)关键实现

      我们为控件提供了这么多东西,剩下的事情就是要真正去实现功能了

      1.重写DataBind方法

      当控件绑定数据时首先会执行此方法触发DataBinding事件

            //控件执行绑定时执行
            public override void DataBind()
            
    {

                
    base.OnDataBinding(EventArgs.Empty);

                
    //移除控件
                Controls.Clear();
                
    //清除视图状态信息
                ClearChildViewState();

                
    //创建一个带或不带指定数据源的控件层次结构
                CreateControlHierarchy(true);
                ChildControlsCreated 
    = true;

                TrackViewState();
            }

      2.CreateControlHierarchy方法

    /// <summary>
    /// 创建一个带或不带指定数据源的控件层次结构
    /// </summary>
    /// <param name="useDataSource">指示是否要使用指定的数据源</param>
    //注意:当第二次执行数据绑定时,会执行两遍
    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)
    {
    //保存项数量
    ViewState["ItemCount"] = ((dataSource != null) ? count : -1);
    }
    }
    //创建项
    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;
    }

      CreateItem方法辅助用于创建项模板,此处注意事件触发顺序,上面已经提到过。此方法根据项索引创建控件中不同的Item项 ,ViewState["ItemCount"]表示项的数量,第一次触发时或者重新执行DataBind方法时方法参数为true,并在初始化以后(回发期间)CreateChildControls方法会调用此方法,其参数为false。数据源不再是实际的数据源,而是新定义的DummyDataSource,其主要实现了一个迭代

    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;
    }
    }
    }

      原因很明显,为了减少对数据源的访问,所以我们平时操作数据的时候,必须重新执行DataBind方法,原因就在此。好了,到了这里差不多主要的事情我们已经完成.接着把剩下的也完成

      3.呈现

      又到了Render方法这里了,此方法体只要执行了PrepareControlHierarchy方法,不同的方法做不同的事情,CreateControlHierarchy方法根据索引值指定了不同的项,PrepareControlHierarchy则为不同项呈现不同的样式效果

    //为不同类型项加载样式
    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);
    }

      终于差不多了,经过这么多步骤,我们终于完成了,让我们来使用控件,看一下效果
      又完成一个并不完美的控件,本来还该继续下去的,怕篇幅太大,到这里还没结束,只是刚开始,下次我们继续

    上一篇:asp.net控件开发基础(16)

    下一篇:asp.net控件开发基础(18)
  • 相关阅读:
    java 配置Apache,Tomcat的gzip压缩功能
    java并发编程中CountDownLatch和CyclicBarrier的使用
    Java常用工具包 Jodd
    Servlet 3特性:异步Servlet
    tomcat7安装
    2013-Hessian
    使用commons configuration管理配置文件
    2013-JVisualVM远程监听服务器内存进程
    Jquery复选框
    Jquery选择器总结
  • 原文地址:https://www.cnblogs.com/waw/p/2162808.html
Copyright © 2011-2022 走看看