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

简介:   本篇继续上篇的讨论,可能大家已经在使用asp.net2.0了,DataSource属性不再使用,而是跟数据源控件搭配使用.现在讨论的绑定技术都是基于1.1版本,先熟悉一下,本质上是一样的,这样一步步的学习.对以后绝对有帮助.因为当你使用数据源控件,只需要设置一个DataSourceID,方便的同时你是否知道数据源控件帮你做了什么事情,如果你想觉的够用了,可以不用了解,但我相信你一定会有需求。

  本篇继续上篇的讨论,可能大家已经在使用asp.net2.0了,DataSource属性不再使用,而是跟数据源控件搭配使用.现在讨论的绑定技术都是基于1.1版本,先熟悉一下,本质上是一样的,这样一步步的学习.对以后绝对有帮助.因为当你使用数据源控件,只需要设置一个DataSourceID,方便的同时你是否知道数据源控件帮你做了什么事情,如果你想觉的够用了,可以不用了解,但我相信你一定会有需求。上篇最后说过了,讨论还刚刚开始,我们大致把核心的方法都写出来了.下面我们继续。

  一.控件对比

  我们可以使用上篇制作的TemplatedList控件跟内置控件做一下对比异同。在2.0未到来的时候,我们只有Repeater,DataList,DataGrid,现在我们也根据这三个控件进行讨论,下面把TemplatedList与DataList进行对比

  (1)布局样式没DataList多...

  (2)模板没DataList多...

  (3)TemplatedList没ItemCollection

  (4)TemplatedList没有预定义Command事件(如EditCommand,UpdateCommand等)

  或者还有更多的,上面的都是次要的,布局上面我们可以改善,我们也可以添加ItemCollection,也可以预定义Command事件,但发现TemplatedList跟内置的绑定控件有几个跟数据操作严重的不同点

  (1)DataSource属性类型不同  IEnumerable和Object

  为什么要将其类型设置为Object呢?

  IEnumerable支持Array,ArrayList等返回类型,但却不支持DataSet类型,这是一个很严重的问题,设置其类型为Object,可以让控件支持更广泛的数据源(当然也要根据需求)这个是本次讨论的重点

  (2)DataMember 

  其用于指定数据源的特定表,由于DataSet的介入,其可能含有多个表,所以也就有了这个属性,否则的话就不需要他

  (3)DataKeyField键字段

  由于预定义Command事件的介入,实现对数据的操作,DataKeyField用于帮助数据特定记录的操作

  二.确定目标

  根据上面的对比,我们已经知道接下来要做什么了,要让控件DataSouce属性支持更多的数据源(只要还是DataSet)

  本次的demo我们将要模仿Repeater来制作,为什么不用TemplatedList?因为这样我们可以对更多控件的实现更加的熟悉,这样在使用内置控件的时候,你将明白的更透彻.此处的demo来自Building ASP.NET Server Controls书中的例子

  Repeater与TemplatedList的异同

  不同点

  大家都知道Repeater可以灵活的进行布局,所以去掉了模板样式属性,我们为其添加了多个模板属性,Repeater控件没有预定义Command事件,所以不需要DataKeyField属性.还为Repeater定义了TemplatedListmy没有的ItemCollection集合,当然也可以为TemplatedList添加这个集合最大的不同。Repeater支持DataSet,TemplatedList不支持。

  相同点

  都是数据绑定控件,所以里面很多的实现方法几乎相同,如果你看过TemplatedList的实现,再看Repeater的代码,基本没有难度,Repeater的实现比TemplatedList还要简单。

  好了,下面我们开始吧.

  三.实现

  1.为数据控件做好准备

  几乎跟上篇一样,所以不再介绍

  2.编写Repeater

  (1)定义成员属性和事件

 
  
private object dataSource;
/// <summary>
/// 绑定的列表的数据源
/// </summary>
[Category( " Data " ), Description( " 绑定的列表的数据源 " ),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
DefaultValue(
null ), Bindable( true )]
public object DataSource
{
get
{
return dataSource;
}
set
{
if ((value is IEnumerable) || (value is IListSource) || (value == null ))
dataSource
= value;
else
throw new Exception( " 错误的数据源类型 " );
}
}

/// <summary>
/// 当数据绑定到列表数据源时要提取的数据成员
/// </summary>
[Category( " Data " ), Description( " 当数据绑定到列表数据源时要提取的数据成员 " )]
public virtual string DataMember
{
get
{
object member = ViewState[ " DataMember " ];
if (member == null )
return string .Empty;
else
return ( string )member;
}
set
{
ViewState[
" DataMember " ] = value;
}
}
主要的变化在于DataSource,类型更改为 object其对传入的数据源进行判断,另外还加入了DataMember属性

  (2)关键实现

  1.因为Repeater模板不具有样式属性,所以去掉了PrepareControlHierarchy方法。

  2.由于不涉及到复杂的样式属性,所以不必重载视图状态管理的三个方法。

  这两点就可以让控件减少很多代码的编写

  3.CreateControlHierarchy方法和CreateItem方法

  Repeater模板的实现方法和TemplatedList稍有不同,但变化不大,应该容易理解.看下面代码

 
  
/// <summary>
/// 创建控件各种项
/// </summary>
/// <param name="itemIndex"></param>
/// <param name="itemType"></param>
/// <param name="dataBind"></param>
/// <param name="dataItem"></param>
/// <returns></returns>
private RepeaterItem CreateItem( int itemIndex, ListItemType itemType, bool dataBind, object dataItem)
{
ITemplate selectedTemplate;
// 根据不同类型创建不同项
switch (itemType)
{
case ListItemType.Header:
selectedTemplate
= headerTemplate;
break ;
case ListItemType.Item:
selectedTemplate
= itemTemplate;
break ;
case ListItemType.AlternatingItem:
selectedTemplate
= alternatingItemTemplate;
break ;
case ListItemType.Separator:
selectedTemplate
= separatorTemplate;
break ;
case ListItemType.Footer:
selectedTemplate
= footerTemplate;
break ;
default :
selectedTemplate
= null ;
break ;
}

if ((itemType == ListItemType.AlternatingItem) &&
(alternatingItemTemplate
== null ))
{
selectedTemplate
= itemTemplate;
itemType
= ListItemType.Item;
}

RepeaterItem item
= new RepeaterItem(itemIndex, itemType, dataItem);

if (selectedTemplate != null )
{
selectedTemplate.InstantiateIn(item);
}

OnItemCreated(
new RepeaterItemEventArgs(item));

Controls.Add(item);

if (dataBind)
{
item.DataBind();
OnItemDataBound(
new RepeaterItemEventArgs(item));
}
return item;
}

private ArrayList items = null ;
private void CreateControlHierarchy( bool useDataSource)
{
items
= new ArrayList();
IEnumerable ds
= null ;

if (HeaderTemplate != null )
{
RepeaterItem header
= CreateItem( - 1 , ListItemType.Header, false , null );
}

int count = - 1 ;
if (useDataSource)
{
// 解析DataSource
ds = (IEnumerable)DataSourceHelper.ResolveDataSource(DataSource,
DataMember);
}
else
{
count
= ( int )ViewState[ " ItemCount " ];
if (count != - 1 )
{
ds
= new DummyDataSource(count);
}
}

if (ds != null )
{
int index = 0 ;
count
= 0 ;
RepeaterItem item;
ListItemType itemType
= ListItemType.Item;

foreach ( object dataItem in (IEnumerable)ds)
{
if (index != 0 )
{
RepeaterItem separator
= CreateItem( - 1 , ListItemType.Separator, false , null );
}

item
= CreateItem(index, itemType, useDataSource, dataItem);
items.Add(item);
index
++ ;
count
++ ;

if (itemType == ListItemType.Item)
itemType
= ListItemType.AlternatingItem;
else
itemType
= ListItemType.Item;
}
}

if (FooterTemplate != null )
{
RepeaterItem footer
= CreateItem( - 1 , ListItemType.Footer, false , null );
}

if (useDataSource)
{
ViewState[
" ItemCount " ] = ((ds != null ) ? count : - 1 );
}
}
其中最大的变化在于这里,因为还需要支持DataSet, DataSourceHelper类负责解析传入的数据源DataSouce进行解析
             if  (useDataSource)
            
{
                
//解析DataSource
                ds = (IEnumerable)DataSourceHelper.ResolveDataSource(DataSource,
                   DataMember);
            }
  下面我们来重点看 DataSourceHelper类,DataSourceHelper类可谓是这一篇的重头戏,关键就在于这里的理解.这里搞明白了,才算是明白.一起来看吧
 
  
// <summary>
/// 一个解析DataSource的辅助类
/// </summary>
public class DataSourceHelper
{
public static object ResolveDataSource( object dataSource, string dataMember)
{
如果数据源为空,则返回空值

如果数据源不为空,且为IEnumerable类型,则返回IEnumerable

如果数据源不为空,且为IListSource类型,则返回IListSource
return null ;

}
}

 

  这个辅助类判断太多,刚看会看晕掉的,所以在if判断这里把代码折叠起来,有助于理解。这里有几个类可能没见过,我们把关键用到的类一一列出来,希望大家查查MSDN

  1.IListSource  向对象提供返回可以绑定到数据源列表的功能

  2.ITypedList   提供发现可绑定列表架构的功能,其中可用于绑定的属性不同于要绑定到的对象的公共属性

  3.PropertyDescriptor  提供类上的属性的抽象化

  4.PropertyDescriptorCollection 表示 PropertyDescriptor 对象的集合

  下面开始

  (1).首先如果传入的数据源类型是IEnumerable的话,很好,可以直接返回

             if  (dataSource  is  IEnumerable)
            
{
                
return (IEnumerable)dataSource;
            }
(2).转化实现 IListSource接口的类 

  虽然传入的类型非IEnumerable,如DataSet类实现了IListSource接口,其目的就是使用此接口的GetList方法返回一个IList(IList继承IEnumerable,可以进行数据绑定),大家可以参考MSDN的原话

                IList list  =   null ;
                IListSource listSource 
=  (IListSource)dataSource;
                list 
=  listSource.GetList();
假设传入的是DataSet,list将会得到System.Data.DataViewManager集合   ,DataViewManager是什么呢?为默认DataTable默认的DataViewSettingCollection

  DataViewSettingCollection是什么呢?表示DataTable的DataViewSetting的集合

  DataViewSetting是什么呢?表示从 DataViewManager 创建的 DataView 的 的默认设置

  上面的我们不熟,DataView大家应该熟悉,其可以对数据进行排序,过滤等。DataViewManager为一个默认的DataView设置集合,不知这样是否可以理解的好些。我们的目的则是将其转化到IEnumerable类型,继续DataViewManager实现了ITypedList接口。我们需要将DataViewManager(即list)转化到ITypedList ,为什么?ITypedList的GetItemProperties方法将帮助你获取DataView数据绑定的数据对象,而非DataView本身属性。

  ITypedList的GetItemProperties方法绑定数据的每项属性的PropertyDescriptorCollection集合,PropertyDescriptorCollection表示PropertyDescriptor集合,PropertyDescriptor这个类很好玩,等同于属性的说明书,即用了.net的反射技术,大家可以尝试一下,其实以前也用过这个类.下面来看代码片段

// 提供发现可绑定列表架构的功能,其中可用于绑定的属性不同于要绑定到的对象的公共属性
  ITypedList typedList  =  (ITypedList)list;
  
// 返回表示用于绑定数据的每项上属性集合
    PropertyDescriptor[] pd  =   new  PropertyDescriptor[ 0 ];
    PropertyDescriptorCollection propDescCol 
=
    typedList.GetItemProperties(pd);  
// was (null)
   
// 如果属性说明符数目为0
    if  (propDescCol.Count  ==   0 )
   
throw   new  Exception( " ListSource without DataMembers " );
  GetItemProperties方法传入了一个PropertyDescriptor的数组,大家可能注意到了传入的数组为一个空数组,你还可以传入一个空引用
PropertyDescriptorCollection propDescCol  =
                       typedList.GetItemProperties(
null );   // was (null)
  如果你为DataTable创建了 DataView,将调用空引用返回DataSet中的一个DataTable,其将返回一个表集合列的属性描述符,继续看下去,该到DataMember出场的时候了,DataMember可以选择数据集中的特定表,如何不设置DataMember,将获取默认表,看下面代码片段
 
  
#region 判断dataMember字符数给propDesc赋值
// 获取属性描述符
// 若不指定dataMember属性则获取默认数据成员
if ((dataMember == null ) || (dataMember.Length < 1 ))
{
propDesc
= propDescCol[ 0 ];
}
else
// 尝试在属性集合中寻找数据成员
propDesc = propDescCol.Find(dataMember, true );

#endregion

if (propDesc == null )
throw new Exception( " ListSource missing DataMember " );

  这样我们就得到了一个DataTablePropertyDescriptor属性描述符,继续

                     object  listitem  =  list[ 0 ];

                    
// 获取组件属性当前值
                     object  member  =  propDesc.GetValue(listitem);

                    
if  ((member  ==   null ||   ! (member  is  IEnumerable))
                        
throw   new  Exception( " ListSource missing DataMember " );

                    
return  (IEnumerable)member;

  此处实现原理:

  DataViewManager会在其DataSet中的DataTableCollection中搜索datamember的值进行匹配,看下图,做这么多事情,我们一直在转换

  注:GetValue用法

        PropertyDescriptorCollection properties  =  TypeDescriptor.GetProperties(Button1);
        PropertyDescriptor pd 
=  properties.Find( " Text " false );
        Button b
= new  Button();
        b.Text 
=   " cc " ;
        
object  c = pd.GetValue(b);
        Response.Write(c);
        
// return cc

  用GetValue方法获取listitem属性值,此属性跟datamember匹配,最后member得到的是一个DataView,DataView实现了IEnumerable,现在终于可以转换了。到此为止就结束了,现在你可以成功的传入DataSet了。

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

下一篇:asp.net控件开发基础(19)
相关实践学习
基于Hologres轻松玩转一站式实时仓库
本场景介绍如何利用阿里云MaxCompute、实时计算Flink和交互式分析服务Hologres开发离线、实时数据融合分析的数据大屏应用。
阿里云实时数仓实战 - 项目介绍及架构设计
课程简介 1)学习搭建一个数据仓库的过程,理解数据在整个数仓架构的从采集、存储、计算、输出、展示的整个业务流程。 2)整个数仓体系完全搭建在阿里云架构上,理解并学会运用各个服务组件,了解各个组件之间如何配合联动。 3&nbsp;)前置知识要求 &nbsp; 课程大纲 第一章&nbsp;了解数据仓库概念 初步了解数据仓库是干什么的 第二章&nbsp;按照企业开发的标准去搭建一个数据仓库 数据仓库的需求是什么 架构 怎么选型怎么购买服务器 第三章&nbsp;数据生成模块 用户形成数据的一个准备 按照企业的标准,准备了十一张用户行为表 方便使用 第四章&nbsp;采集模块的搭建 购买阿里云服务器 安装 JDK 安装 Flume 第五章&nbsp;用户行为数据仓库 严格按照企业的标准开发 第六章&nbsp;搭建业务数仓理论基础和对表的分类同步 第七章&nbsp;业务数仓的搭建&nbsp; 业务行为数仓效果图&nbsp;&nbsp;
目录
相关文章
|
1月前
|
SQL 开发框架 数据可视化
企业应用开发中.NET EF常用哪种模式?
企业应用开发中.NET EF常用哪种模式?
|
2月前
|
开发框架 JavaScript 前端开发
5个.NET开源且强大的快速开发框架(帮助你提高生产效率)
5个.NET开源且强大的快速开发框架(帮助你提高生产效率)
|
10天前
|
开发框架 前端开发 JavaScript
采用C#.Net +JavaScript 开发的云LIS系统源码 二级医院应用案例有演示
技术架构:Asp.NET CORE 3.1 MVC + SQLserver + Redis等 开发语言:C# 6.0、JavaScript 前端框架:JQuery、EasyUI、Bootstrap 后端框架:MVC、SQLSugar等 数 据 库:SQLserver 2012
|
1月前
|
数据安全/隐私保护 Windows
.net三层架构开发步骤
.net三层架构开发步骤
13 0
|
1月前
深入.net平台的分层开发
深入.net平台的分层开发
59 0
|
1月前
|
开发框架 中间件 .NET
C# .NET面试系列七:ASP.NET Core
## 第一部分:ASP.NET Core #### 1. 如何在 controller 中注入 service? 在.NET中,在ASP.NET Core应用程序中的Controller中注入服务通常使用<u>依赖注入(Dependency Injection)</u>来实现。以下是一些步骤,说明如何在Controller中注入服务: 1、创建服务 首先,确保你已经在应用程序中注册了服务。这通常在Startup.cs文件的ConfigureServices方法中完成。例如: ```c# services.AddScoped<IMyService, MyService>(); //
63 0
|
1月前
|
开发框架 前端开发 .NET
C# .NET面试系列六:ASP.NET MVC
<h2>ASP.NET MVC #### 1. MVC 中的 TempData\ViewBag\ViewData 区别? 在ASP.NET MVC中,TempData、ViewBag 和 ViewData 都是用于在控制器和视图之间传递数据的机制,但它们有一些区别。 <b>TempData:</b> 1、生命周期 ```c# TempData 的生命周期是短暂的,数据只在当前请求和下一次请求之间有效。一旦数据被读取,它就会被标记为已读,下一次请求时就会被清除。 ``` 2、用途 ```c# 主要用于在两个动作之间传递数据,例如在一个动作中设置 TempData,然后在重定向到另
99 5
|
2月前
|
开发框架 前端开发 .NET
福利来袭,.NET Core开发5大案例,30w字PDF文档大放送!!!
为了便于大家查找,特将之前开发的.Net Core相关的五大案例整理成文,共计440页,32w字,免费提供给大家,文章底部有PDF下载链接。
33 1
福利来袭,.NET Core开发5大案例,30w字PDF文档大放送!!!
|
2月前
|
SQL 开发框架 前端开发
ASP.NET WEB项目中GridView与Repeater数据绑定控件的用法
ASP.NET WEB项目中GridView与Repeater数据绑定控件的用法
34 0
|
3月前
|
SQL 开发框架 JavaScript
分享33个ASP.NET电子商务源码和40个ASP.NET控件组件源码,总有一款适合您
分享33个ASP.NET电子商务源码和40个ASP.NET控件组件源码,总有一款适合您
29 0