只是人各有看法,当我写出一篇文章时,我只是希望:
1:如果你还不懂,请看写法,了解想法。其实纵观我一直写来的200多篇文章,基本都可以看出那么点痕迹:
一:没有水文。一、Winform下的DataGridView不支持使用DataReader绑定
1:问题产生
在 CYQ.Data 框架 进行到V1.5版本要支持Winform时,曾遇到一个问题,就是无法绑定DataGridView。2:思考分析试验
MDataTable走的是DataReader方式实现的绑定,除非DataReader无法绑定DataGridView,不然就是自己实现有问题。3:得出结论
DataReader方式都无法绑定Winform下的DataGridView,我这继承之DataReader的实现方式也就更无从实现绑定了。二、DataTable很强大,支持Web又支持Winform
1:分析绑定原理
在以前的MDataTable实现绑定原理篇中,我们研究出要实现绑定,有两种方式:2:深入DataTable绑定原理
我们通过Reflector反编绎看下DataTable继承实现的接口:
- public class DataTable : MarshalByValueComponent, IListSource, ISupportInitializeNotification, ISupportInitialize, ISerializable, IXmlSerializable
几乎都是我们平常没用到的接口,不理先,我们关注IListSource怎么实现绑定的。如果自己看一下IListSource要实现的接口有几个方法:
- public interface IListSource
- {
- // Methods
- IList GetList();
- // Properties
- bool ContainsListCollection { get; }
- }
就两个,太容易了,接着我们要在DataTable 6000多行的代码中找到IListSource的实现,查找是最好的方法
- //DataTable的实现
- bool IListSource.ContainsListCollection
- {
- get { return false; }
- }
- IList IListSource.GetList()
- {
- return this.DefaultView;
- }
GetList接口没事就返回了个默认视图,又要切进去看视图了。
- public DataView DefaultView
- {
- get
- {
- DataView defaultView = this.defaultView;
- if (defaultView == null)
- {
- if (this.dataSet != null)
- {
- defaultView = this.dataSet.DefaultViewManager.CreateDataView(this);
- }
- else
- {
- defaultView = new DataView(this, true);
- defaultView.SetIndex2("", DataViewRowState.CurrentRows, null, true);
- }
- defaultView = Interlocked.CompareExchange<DataView>(ref this.defaultView, defaultView, null);
- if (defaultView == null)
- {
- defaultView = this.defaultView;
- }
- }
- return defaultView;
- }
- }
切进去就一大堆,实在没心情看下去,省略中间看个头与尾,只知道返回了个DataView。
- public class DataView : MarshalByValueComponent, IBindingListView, IBindingList, IList, ICollection, IEnumerable, ITypedList, ISupportInitializeNotification, ISupportInitialize
忽悠:
又是神马般的一堆接口,内部代码太多,实在没心情看;神马啊神马,没点头绪,完全找不到绑定的重点,难道说,随便找个IList返回的类就行了?于是让MDataTable实现IListSource接口,试试看:
- public class MDataTable : IDataReader, IEnumerable,System.ComponentModel.IListSource
实现接口:
- public IList GetList()
- {
- return Rows;
- }
接着忽悠:
好说我的Rows也是继承自List<xxx>的,试着绑定~~结果很飘逸,出来完全不是我想象~~。
继承折腾DataView,传说DataView也能直接绑定控件的,yo~~有一丝想法。
于是看一下其实现IList接口的源码,发现一堆都在操作DataRowView
- public class DataRowView : ICustomTypeDescriptor, IEditableObject, IDataErrorInfo, INotifyPropertyChanged
没法忽悠了:
你个XX,从DataTable-》DataView-》DataRowView,再转我头就晕了~~。
又是一堆很陌生的接口,于是到这里,我几乎停止了脚步,因为我分析不下去了~~。
上WC仔细从头想过:
对于IList<实体>绑定,所有的属性都会被认为是列名,其值为行的值。而对于DataTable,里面又是怎么认识出列名和分析出值的呢?
1:从DataTable中,我们看到一丝列名提取的相关方法,只是返回->DataRow。
2:从DataRow中也看不到提取列名的方法,其关键性的IList接口的相关实现引出了->DataRowView。
3:DataRowView?是神秘的所在?一堆继承的接口也是很陌生。
回头继续搜索:
转换思路继续大量搜索:换了很多关键字,搜中文又搜E文。结果尽是一堆自定义控件开发的东东,结果印象中在某一篇的googleE文的“网页快照”中发现一段E文,原文不知是哪了,上次都记得只能打开快照,现在估计能快照都没了,按想象翻译出来的中文大致为:
DataTable能实现其绑定,是因为其实现了ICustomTypeDescriptor,从而获得其属性。
偶滴神啊~能从千军万马的E文中,扫到几个关键字不容易啊!!!
如果回过头看上面的DataRowView,就会发现,正好,它实现了接口ICustomTypeDescriptor,
只是遥想当年,我并不像现在写文这么冷静,我当初早把Reflector关掉了,哪还记得DataRowView实现了ICustomTypeDescriptor,
再说ICustomTypeDescriptor对我又是那么的陌生,是那么的陌生,很陌生。。。
秘密已经出来了:
ICustomTypeDescriptor接口,一个移动控件开发人员经常打交道的接口,对于我们却极为陌生的接口。3:浅入ICustomTypeDescriptor
当初我通过大量的搜索,试图找到相关的应用示例,因为那时我不知道DataRowView,要是知道,我就不用那么辛苦去搜文章了。不过此文就不走弯路了,直接分析DataRowView,对于 ICustomTypeDescriptor接口,有很多方法:
- public interface ICustomTypeDescriptor
- {
- // Methods
- AttributeCollection GetAttributes();
- string GetClassName();
- string GetComponentName();
- TypeConverter GetConverter();
- EventDescriptor GetDefaultEvent();
- PropertyDescriptor GetDefaultProperty();
- object GetEditor(Type editorBaseType);
- EventDescriptorCollection GetEvents();
- EventDescriptorCollection GetEvents(Attribute attributes);
- PropertyDescriptorCollection GetProperties();
- PropertyDescriptorCollection GetProperties(Attribute attributes);
- object GetPropertyOwner(PropertyDescriptor pd);
- }
不过基本是摆设,只因用不到,除了一个接口方法:GetProperties(Attribute attributes)
于是我们分析DataRowView对此接口的实现:
- PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute attributes)
- {
- if (this.dataView.Table == null)
- {
- return zeroPropertyDescriptorCollection;
- }
- return this.dataView.Table.GetPropertyDescriptorCollection(attributes);
- }
继续深入:
- internal PropertyDescriptorCollection GetPropertyDescriptorCollection(Attribute attributes)
- {
- if (this.propertyDescriptorCollectionCache == null)
- {
- int count = this.Columns.Count;
- int num4 = this.ChildRelations.Count;
- PropertyDescriptor properties = new PropertyDescriptor[count + num4];
- for (int i = 0; i < count; i++)
- {
- properties[i] = new DataColumnPropertyDescriptor(this.Columns[i]);
- }
- for (int j = 0; j < num4; j++)
- {
- properties[count + j] = new DataRelationPropertyDescriptor(this.ChildRelations[j]);
- }
- this.propertyDescriptorCollectionCache = new PropertyDescriptorCollection(properties);
- }
- return this.propertyDescriptorCollectionCache;
- }
关键定位,只是返回一组:DataColumnPropertyDescriptor 。
那DataColumnPropertyDescriptor是什么?继续深入:
- internal DataColumnPropertyDescriptor(DataColumn dataColumn) : base(dataColumn.ColumnName, null)
- {
- this.column = dataColumn;
- }
两行代码,那个base是啥?是PropertyDescriptor ,实现很简单,把列名传过去就行了,至此,就结束了。不知道有多少会看到这里,估计本文大伙也就是扫下来,除非某天要应用到,不然只是忽悠下眼球了。
总结下具体实现ICustomTypeDescriptor接口方法:
1:继承实现接口方法。三、绑定原理分析完,MDataTable模仿出击
1:MDataTable继承IListSource接口实现
- #region IListSource 成员
- public bool ContainsListCollection
- {
- get
- {
- return true;
- }
- }
- public IList GetList()
- {
- return Rows;
- }
- #endregion
2:MDataRow继承ICustomTypeDescriptor接口实现
A:先实现自定义属性描述类
自定义属性描述类MDataProperty
- internal class MDataProperty : System.ComponentModel.PropertyDescriptor
- {
- private MDataCell cell = null;
- public MDataProperty(MDataCell mdc, Attribute attrs)
- : base(mdc._CellStruct.ColumnName, attrs)
- {
- cell = mdc;
- }
- public override bool CanResetValue(object component)
- {
- return false;
- }
- public override Type ComponentType
- {
- get
- {
- return typeof(MDataCell);
- }
- }
- public override object GetValue(object component)
- {
- return ((MDataRow)component)[cell._CellStruct.ColumnName].Value;
- }
- public override bool IsReadOnly
- {
- get
- {
- return false;
- }
- }
- public override Type PropertyType
- {
- get { return cell._CellStruct.ValueType; }
- }
- public override void ResetValue(object component)
- {
- }
- public override void SetValue(object component, object value)
- {
- cell.Value = value;
- }
- public override bool ShouldSerializeValue(object component)
- {
- return true;
- }
- public override bool IsBrowsable
- {
- get
- {
- return true;
- }
- }
- }
B:实现重点方法GetProperties(Attribute attributes)
- int index = 0;
- PropertyDescriptorCollection properties;
- public PropertyDescriptorCollection GetProperties(Attribute attributes)
- {
- if (index == 1)
- {
- return properties;
- }
- index++;
- properties = new PropertyDescriptorCollection(null);
- foreach (MDataCell mdc in this)
- {
- properties.Add(new MDataProperty(mdc, null));
- }
- return properties;
- }
OK,此至,MDataTable顺利完成了对Winform下DataGridView的支持。本文原标题:CYQ.Data 轻量数据层之路 MDataTable绑定Winform之DataGridView 原理高级篇(三十一)
四、总结
微软很强大,MB的Silverlight不支持DataTable的绑定,难道我又要去追随?研究其绑定本质?