您的位置:首页 > 编程语言 > ASP

一起谈.NET技术,asp.net控件开发基础(17)

2011-09-01 23:47 821 查看
  本篇将开始介绍如自定义数据绑定控件,这里感谢很多人的支持,有你们的支持很高兴。这里首先需要大家熟悉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,请看下面类图
//获取上次选中项
int oldSelectedIndex = SelectedIndex;
ViewState["SelectedIndex"] = value;

当第一次更改SelectedIndex属性时只执行下列代码([b]将此项标记为选中项
),因为初始化时的没有oldSelectedIndex,不需要恢复样式

//第一次执行此项,并一直执行
if ((value != -1) && (table.Rows.Count > value))
//控件执行绑定时执行
public override void DataBind()
{

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


  终于差不多了,经过这么多步骤,我们终于完成了,让我们来使用控件,看一下效果


  又完成一个并不完美的控件,本来还该继续下去的,怕篇幅太大,到这里还没结束,只是刚开始,下次我们继续

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: