天天看點

一起談.NET技術,DataTable 深入解析資料源綁定原理之進階篇

  前言

隻是人各有看法,當我寫出一篇文章時,我隻是希望:

1:如果你還不懂,請看寫法,了解想法。

2:如果你已懂,略過寫法,請看想法。

  其實縱觀我一直寫來的200多篇文章,基本都可以看出那麼點痕迹:

一:沒有水文。

二:沒有華麗理論型的文章。

三:實戰型文章很多。

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

  一、Winform下的DataGridView不支援使用DataReader綁定

  1:問題産生

  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接口沒事就傳回了個預設視圖,又要切進去看視圖了。

一起談.NET技術,DataTable 深入解析資料源綁定原理之進階篇
一起談.NET技術,DataTable 深入解析資料源綁定原理之進階篇

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

  忽悠:

又是神馬般的一堆接口,内部代碼太多,實在沒心情看;

我隻想知道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,要是知道,我就不用那麼辛苦去搜文章了。

如果你搜尋此接口,你會發現一堆的文章都是說移動控件開發,我就是從移動控件開發中很辛苦的挖了點示例實作了。

一起談.NET技術,DataTable 深入解析資料源綁定原理之進階篇
一起談.NET技術,DataTable 深入解析資料源綁定原理之進階篇

public interface ICustomTypeDescriptor

    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);

  于是我們分析DataRowView對此接口的實作:

一起談.NET技術,DataTable 深入解析資料源綁定原理之進階篇
一起談.NET技術,DataTable 深入解析資料源綁定原理之進階篇

PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes)

    if (this.dataView.Table == null)

        return zeroPropertyDescriptorCollection;

    return this.dataView.Table.GetPropertyDescriptorCollection(attributes);

  繼續深入:

一起談.NET技術,DataTable 深入解析資料源綁定原理之進階篇
一起談.NET技術,DataTable 深入解析資料源綁定原理之進階篇

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;

internal DataColumnPropertyDescriptor(DataColumn dataColumn) : base(dataColumn.ColumnName, null)

    this.column = dataColumn;

  總結下具體實作ICustomTypeDescriptor接口方法:

1:繼承實作接口方法。

2:重點實作GetProperties(Attribute[] attributes)方法。

3:需要自定義屬性描述類,而這自定義的屬性描述類需要繼承自抽象基類PropertyDescriptor。

4:GetProperties傳回的是自定義屬性描述類的集合。

  三、綁定原理分析完,MDataTable模仿出擊

  1:MDataTable繼承IListSource接口實作

一起談.NET技術,DataTable 深入解析資料源綁定原理之進階篇
一起談.NET技術,DataTable 深入解析資料源綁定原理之進階篇

       #region IListSource 成員

        public bool ContainsListCollection

            get

                return true;

        public IList GetList()

            return Rows;

        #endregion

  2:MDataRow繼承ICustomTypeDescriptor接口實作

  A:先實作自定義屬性描述類

一起談.NET技術,DataTable 深入解析資料源綁定原理之進階篇
一起談.NET技術,DataTable 深入解析資料源綁定原理之進階篇

自定義屬性描述類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

                return typeof(MDataCell);

        public override object GetValue(object component)

            return ((MDataRow)component)[cell._CellStruct.ColumnName].Value;

        public override bool IsReadOnly

                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

  B:實作重點方法GetProperties(Attribute[] attributes)

一起談.NET技術,DataTable 深入解析資料源綁定原理之進階篇
一起談.NET技術,DataTable 深入解析資料源綁定原理之進階篇

        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<實體>型就可以直接綁定了。