asp.net控件开发基础(17) --------初识数据绑定控件

本文介绍如何自定义ASP.NET数据绑定控件,包括定义模板控件、实现数据绑定过程、设置样式属性和触发相关事件。通过实例演示控件的创建与使用。
首先请下载 示例代码
        
本篇将开始介绍如自定义数据绑定控件,这里感谢很多人的支持,有你们的支持很高兴.

这里首先需要大家熟悉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)定义控件成员属性

        静态变量
静态变量

        成员变量
成员变量

        控件属性
控件属性
成员如下(可以看上面类图)

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)定义控件成员事件

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

(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[
0is 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);
        }



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



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


事件
事件
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值