本篇文章给大家分享的是有关ASP.NET中怎么实现一个数据绑定控件,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。
ASP.NET数据绑定控件一.回顾
如果你使用过ASP.NET内置的数据控件(如DataList,Repeater),你一定会这么做
设置数据源 DataSource属性
调用数据绑定 DataBind方法
在控件的不同模板内使用绑定语法显示数据
这三步应该是必须要做的
其他更多的
你可能需要对绑定的数据进行统一的一些操作(如时间格式化),或者对数据的某一项进行操作(对某一项进行格式化),或者需要触发模板控件内的一些事件(如databound事件).
根据上面的一些需求,我们需要这样做
对绑定的数据进行统一的一些操作: 为数据绑定控件定义Item项(表示列表的一条数据, 如Repeater的RepeaterItem)
对数据的某一项进行操作: 因为定义了Item项,那你肯定需要一个ItemCollection集合,其可以方便的为你检索数据
因为定义了RepeaterItem,原先的EventArgs和CommandEventArgs已经无法满足需求,我们需要自定义委托及其一个为控件提供数据的的ItemEventArgs
上面三点有些并非必须定义,如第2点,还需要根据具体需求来定.但一个完成的控件是需要的.
ASP.NET数据绑定控件二.为数据控件做好准备
这次的demo为不完整的Datalist控件,来源还是MSDN的例子,我们命名为TemplatedList,此控件未定义ItemCollection集合
好了,根据上面的分析我们先为TemplatedList提供项和委托及为事件提供数据的几个EventArgs,请看下面类图
TemplatedListCommandEventArgs为Command事件提供数据
TemplatedListItemEventArgs为一般项提供数据
TemplatedListItem表示TemplatedList的项
ASP.NET数据绑定控件三.编写TemplatedList
TemplatedList主要功能简介
提供一个ItemTemplate模板属性,提供三种不同项样式,ItemCommand 事件冒泡事件及4个事件
实现主要步骤
以下为必须
(1)控件必须实现 System.Web.UI.INamingContainer 接口
(2)定义至少一个模板属性
(3)定义DataSource数据源属性
(4)定义控件项DataItem,即模板的一个容器
(5)重写DataBind 方法及复合控件相关方法(模板控件为特殊的复合控件)
当然还有其他额外的属性,样式,事件
具体实现
下面我们来具体看实现方法
(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
成员如下(可以看上面类图)
三个项样式和三个样式属性
公开DataSource数据源属性,一个模板属性
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)关键实现
我们为控件提供了这么多东西,剩下的事情就是要真正去实现功能了
重写DataBind方法
当控件绑定数据时首先会执行此方法触发DataBinding事件
//控件执行绑定时执行 public override void DataBind() { base.OnDataBinding(EventArgs.Empty); //移除控件 Controls.Clear(); //清除视图状态信息 ClearChildViewState(); //创建一个带或不带指定数据源的控件层次结构 CreateControlHierarchy(true); ChildControlsCreated = true; TrackViewState(); }
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方法,原因就在此
好了,到了这里差不多主要的事情我们已经完成.接着把剩下的也完成
呈现
又到了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中怎么实现一个数据绑定控件,小编相信有部分知识点可能是我们日常工作会见到或用到的。希望你能通过这篇文章学到更多知识。更多详情敬请关注编程网行业资讯频道。