代码改变世界

关于模板控件如何实现多数据源绑定的问题

2007-03-12 13:21  BAsil  阅读(3598)  评论(6编辑  收藏  举报

在读Clinglingboy的asp.net控件开发基础(18)时,Clinglingboy对其进行了重点讲解。可是我感觉在如何将具有IListSource接口的数据源最终转化为DataView说的还不是十分清楚,下面我这一部分再详细的说一下。
首先还是贴一下关键的DataSourceHelper类

DataSourceHelper


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

            if (dataSource is IEnumerable)
            
{
                
return (IEnumerable)dataSource;
            }

 这里像Array、ArrayList、SqlDataReader、DataView等都直接或者间接的实现了IEnumerable接口。

(2)如果传入的类型非IEnumerable,那么代码会判断数据源是否实现了IListSource接口,因为如果实现了IListSource接口,那么我们同样可以利用此接口的GetList方法返回一个IList,而IList继承IEnumerable,同样可以进行数据绑定。当然如果数据源没有实现IEnumerable和IListSource,数据源就不可绑定。
这里像DataTable、DataSet都实现了IListSource接口。
DataTable实现的GetList方法

 IList IListSource.GetList()
 
{
     
return this.DefaultView;
 }

返回了一个DataView
DataSet实现的GetList方法

 IList IListSource.GetList()
 
{
     
return this.DefaultViewManager;
 }

 

返回了一个DataViewManager。

通过判断IListSource中的ContainsListCollection,我们可以知道包含多个DataTable的DataSet还是只有一个DataTable,对于后者,由于已经通过GetList方法得到了它的DataView,而DataView又实现了IEnumerable接口,问题也解决了。

问题现在集中到如何处理DataSet的数据源,我们来看一下DataViewManager类,除了几个public的属性,还有一个DataViewManagerListItemTypeDescriptor类型的Item值得我们注意,后面会讲解此类。同时DataViewManager类实现了ITypedList接口,接下来利用ITypedList.GetItemProperties(object)得到PropertyDescriptorCollection.



我们看一下ITypedList.GetItemProperties(object)的代码,其中关键一句

return ((ICustomTypeDescriptor) new DataViewManagerListItemTypeDescriptor(this)).GetProperties();

看来DataViewManagerListItemTypeDescriptor的GetProperties方法可以得到PropertyDescriptorCollection。此类是Framework的一个内部类,实现了ICustomTypeDescriptor接口。

那么ICustomTypeDescriptor是做什么用的呢。我们来看一下msdn:

ICustomTypeDescriptor 使对象得以提供有关自身的类型信息。通常,当对象需要动态类型信息时使用此接口。相反,TypeDescriptor 提供从元数据获得的静态类型信息。

大家可能对这句话不太明白,我解释一下,这里我用PropertyGrid举例,不熟悉的可以在网上查,实际上我感觉PropertyGrid在和某个类绑定的时候,默认的是用TypeDescriptor 提供从元数据获得的静态类型信息。如下图

 
但是有些情况,你需要用到 PropertyGrid 去绑定一个属性/值的集合,但是这个属性/值的集合并不适合写成一个固定的类。

比如你想用 PropertyGrid 绑定XML 里的数据。或者数据库的某个表。

假设你有 1000 个XML 文件,每个 XML 所取到的属性集合各不一样,你不可能为每个XML 文件都写一个类 。

或者你的某个数据表有1000 条记录,该表有 a 字段的值表示属性名称, b字段的值表示属性值,你不可能写一个类,定义1000个属性。

这时候,我们就希望是否能够将一个动态的属性/值的集合与Property 绑定。通过实现ICustomTypeDescriptor,我们就可以完成动态的属性/值的集合与Property 绑定。这里参考了PropertyGrid 绑定动态的属性与值的集合文章,这篇文章对大家理解ICustomTypeDescriptor会有很大的帮助,文章的代码是VB2005,我用c#2003重新写了一下,这两段代码我会在文章后面给出下载,建议大家先读这篇文章以帮助理解。我把这篇文章的几个类的关键部分列出来。

XProp

 

XPropDescriptor

 

XProps

回到问题上来,在我们实现了ICustomTypeDescriptor,不需要和PropertyGrid绑定,我们可以得到一个PropertyDescriptorCollection。那么就来具体看看对比。

其中上文的XProp -->  DataTable

                 XProps 的GetProperties方法--> ((ITypedList) DataViewManager).GetItemProperties方法

                 XPropDescriptor--> DataTablePropertyDescriptor

大家会看到((ITypedList) DataViewManager).GetItemProperties方法返回了DataTablePropertyDescriptor的PropertyDescriptorCollection集合;XProps的GetProperties方法返回了XPropDescriptor的PropertyDescriptorCollection集合

在DataTablePropertyDescriptor会有一个DataTable的属性,并且该类复写了GetValue方法,取得值,这个和XPropDescriptor中有XProp属性,且复写了GetValue方法是一致的。唯一不同的是XPropDescriptor的GetValue方法只是将具体的XProp的Value返回,而DataTablePropertyDescriptor中的GetValue方法又利用DataTable进一步操作返回了DataView。

我们现在知道ITypedList.GetItemProperties(object)是怎么得到PropertyDescriptorCollection(确切的说是DataTablePropertyDescriptor),我们接着利用propDesc = propDescCol.Find(dataMember, true)去在集合中查找名字为dataMember值也就是具体的表名,以返回待操作的DataTablePropertyDescriptor。在((ICustomTypeDescriptor) new DataViewManagerListItemTypeDescriptor(this)).GetProperties()方法建立集合的时候采用了表名作为名值对的名,大家可以对照代码看看。接下来再看这段代码

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

list是什么?实际上是我们在前面得到的DataViewManager.IListSource listSource = (IListSource)dataSource;
list = listSource.GetList();

由于DataViewManager实现了IList接口,因此我们可以用list[index]的形式取得具体的元素,这里我们看到是取得了item的值,还记得我们前面让大家留意DataViewManager的Item属性,实际上它就是一个DataViewManagerListItemTypeDescriptor。propDesc是一个DataTablePropertyDescriptor,来看一下他的GetValue(object)代码

 

public override object GetValue(object component)
{
DataViewManagerListItemTypeDescriptor descriptor 
= (DataViewManagerListItemTypeDescriptor) component;
return descriptor.GetDataView(this.table);
}
 

而DataViewManagerListItemTypeDescriptor的GetDataView的代码 

internal DataView GetDataView(DataTable table)
{
DataView view 
= new DataView(table);
view.SetDataViewManager(
this.dataViewManager);
return view;
}
 

实际上这一步就是利用DataTable构建DataView,我觉得也可以用其他的方法完成,给DataViewManagerListItemTypeDescriptor增加一个内部的GetDataView方法反而弱化了TypeDescriptor的功能。

到这里,我们就可以返回一个(IEnumberable)DataView了。

PropertyGrid 绑定动态的属性与值的集合文章代码下载:

 

ICustomTypeDescriptorTestVB.zip(VB2005)

ICustomTypeDescriptorTestCSharp.zip(C#2003,其中VB2005使用了范型,改写的时候用了CollectionBase,效果一样)