zoukankan      html  css  js  c++  java
  • DataTable 深入解析数据源绑定原理之高级篇

    前言

    在上篇写了篇 实战系列之天气预报实时采集 ,有个别同志认为没技术含量,也许正如所说。

    只是人各有看法,当我写出一篇文章时,我只是希望:

    1:如果你还不懂,请看写法,了解想法

    2:如果你已懂,略过写法,请看想法

    其实纵观我一直写来的200多篇文章,基本都可以看出那么点痕迹:

    一:没有水文

    二:没有华丽理论型的文章

    三:实战型文章很多

    四:文章尽量面向新手的表述,尽量了

    今天写篇,还是试图从简入手,希望新手也能有收获,有有没“技术含量”,大伙看着说了~~~要是看不懂,你就点下推荐~

    以下为正文

    一:Winform下的DataGridView不支持使用DataReader绑定

    1:问题产生

    在 CYQ.Data 框架 进行到V1.5版本要支持Winform时,曾遇到一个问题,就是无法绑定DataGridView。

    2:思考分析试验

    MDataTable走的是DataReader方式实现的绑定,除非DataReader无法绑定DataGridView,不然就是自己实现有问题。

    因此,做个试验:使用SqlDataReader直接绑定Winform下的DataGridView,发现失败了。

    于是大量搜索,发现DataReader实在无法直接绑定DataGridView,通过数据源控件中转绑定的就算了。

    3:得出结论

    DataReader方式都无法绑定Winform下的DataGridView,我这继承之DataReader的实现方式也就更无从实现绑定了。

    只好另寻方法-》DataGridView支持DataTable,于是要从DataTable入手了。

    二:DataTable很强大,支持Web又支持Winform

     

    1:分析绑定原理

    在以前的MDataTable实现绑定原理篇中,我们研究出要实现绑定,有两种方式:

    一种是实现IEnumerable接口,即当初走的DataReader方式实现的绑定。

    另一种是实现IListSource接口,即走DataTable方式实现的绑定。

    为啥当初不实现DataTable方式的绑定,不就完了,两种都支持~~-_-..现在又得回去折腾IListSource接口的实现

    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(thistrue);
                    defaultView.SetIndex2(
    "", DataViewRowState.CurrentRows, nulltrue);
                }
                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

    忽悠:

    又是神马般的一堆接口,内部代码太多,实在没心情看;
    我只想知道IListSource怎么实现绑定,至于其它有一堆没一堆的我根本不关心,我只要我想要的。
    扫了一眼接口,发现是继承了IList,这和IListSource要求的返回值IList是一致的。

    神马啊神马,没点头绪,完全找不到绑定的重点,难道说,随便找个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:继承实现接口方法
    2:重点实现GetProperties(Attribute[] attributes)方法
    3:需要自定义属性描述类,而这自定义的属性描述类需要继承自抽象基类PropertyDescriptor
    4:GetProperties返回的是自定义属性描述类的集合

    三:绑定原理分析完,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的绑定,难道我又要去追随?研究其绑定本质?

    不追了,MDataTable增加了ToJson方法和ToList
    <实体>方法,可直接用json传过去再用反json系列化解析成List<实体>型就可以直接绑定了。

    本文有点长,不知有几人,看的明白,看的仔细,估计都是刷的一下从头拉到尾了~~~~ 

  • 相关阅读:
    租房子查询练习
    投票练习题
    多条件查询
    查询
    练习---新闻界面
    mysql增删改处理
    挖宝游戏
    mysql数据访问
    练习···表格
    类的使用
  • 原文地址:https://www.cnblogs.com/cyq1162/p/1860031.html
Copyright © 2011-2022 走看看