[Windows Forms] BindingSource使用模式 - Data Binding基礎知識 (二)

  • 13073
  • 0
  • .NET
  • 2015-12-09

[Windows Forms] : BindingSource使用模式 - Data Binding基礎知識 (二)

 

前言 :

 

在文章「[.NET] : BindingSource使用模式 - Data Binding基礎知識 (一)」。

 

介紹了如何將物件的屬性包裝成屬性物件 「PropertyDescriptor」,並用它來做存取、監看變更等工作。
將資料物件的屬性包裝成屬性物件是 Data Binding運作基礎,在了解這個運作之後。
這邊再來討論,Data Binding時會用到的「資料來源」。

 

 

在大部分的書裡描述,Data Binding透過 ADO.NET裡的物件與資料庫做互動,用來顯示及存取資料庫內的資料。
在這架構下,ADO.NET裡的物件是一種 Data Binding的資料來源。
相關資料 : HOW TO:使用 Windows Form BindingSource 元件排序和篩選 ADO.NET 資料

 

 

也有一部份的資料提到的是, Data Binding也可以包裝自訂資料物件,來做自訂資料物件的顯示及存取。
在這架構下,自訂資料物件包裝後也是一種 Data Binding的資料來源。
相關資料 : 具有 ADO.NET 和自訂物件的資料繫結應用程式

 

 

關於 Data Binding的資料來源,細分下去有許多不同的實作與定義。
相關資料可以參考 : Windows Form 支援的資料來源與資料繫結相關的介面

 

 

本篇文章簡略介紹,幾個設計開發 Data Binding用來包裝資料來源用的相關物件。
讓軟體開發人員在設計 Data Binding相關程式碼時,能對物件運作模式有基礎的理解。

 

 

BindingList :

 

在上列文章提供的相關資料裡,能找到大量針對 Data Binding的資料來源定義的介面。
照著資料文件可以實作出,符合自己需求的資料來源物件,但這是一件工作量不小的工作。

 

在 System.ComponentModel命名空間裡,可以找到 BindingList<T>這個物件。
BindingList<T>實作針對 Data Binding的資料來源定義的主要介面。
並且 BindingList<T>是一個泛型類別,可以接受類別 T當作自訂資料物件。

 

 

開發人員可以使用 BindingList<T>,來將自訂資料物件包裝成資料來源。
首先建立自訂資料物件

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace WindowsFormsApplication1
{
    public class County
    {
        // Properties
        public int CountyID { get; set; }

        public string CountyName { get; set; }

        public string CountyDescription { get; set; }


        // Constructor
        public County()
        {
            this.CountyID = 0;
            this.CountyName = string.Empty;
            this.CountyDescription = string.Empty;
        }
    }
}

 

再來建立 DataGridView、BindingNavigator、BindingSource繫結資料

 

image
image
image
image
image
image

 

最後建立資料來源並繫結資料

 

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        BindingList<County> _bindingList = new BindingList<County>();

        public Form1()
        {
            InitializeComponent();

            // Add Item
            County county1 = new County();
            county1.CountyID = 1;
            county1.CountyName = "台北市";
            county1.CountyDescription = "買不起";
            _bindingList.Add(county1);

            County county2 = new County();
            county2.CountyID = 2;
            county2.CountyName = "新北市";
            county2.CountyDescription = "還是買不起";
            _bindingList.Add(county2);

            // Data Binding
            this.countyBindingSource.DataSource = _bindingList;
        }
    }
}

 

完成,看成果。

 

image

 

使用 BindingList<T>有一個地方需要特別注意的,就是關於 AddingNew事件。
AddingNew事件,主要用來通知要建立一個新的資料物件。

 

 

當有處理 AddingNew事件,BindingList<T>會加入 AddingNew事件裡帶回的 NewObject。
修改本文範例為

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace WindowsFormsApplication1
{
    public class County
    {
        // Properties
        public int CountyID { get; set; }

        public string CountyName { get; set; }

        public string CountyDescription { get; set; }


        // Constructor
        public County()
        {
            this.CountyID = 0;
            this.CountyName = string.Empty;
            this.CountyDescription = string.Empty;
        }
    }
}

 

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        BindingList<County> _bindingList = new BindingList<County>();

        public Form1()
        {
            InitializeComponent();

            // Add Item
            County county1 = new County();
            county1.CountyID = 1;
            county1.CountyName = "台北市";
            county1.CountyDescription = "買不起";
            _bindingList.Add(county1);

            County county2 = new County();
            county2.CountyID = 2;
            county2.CountyName = "新北市";
            county2.CountyDescription = "還是買不起";
            _bindingList.Add(county2);

            // EventHandler
            _bindingList.AddingNew += new AddingNewEventHandler(_bindingList_AddingNew);

            // Data Binding
            this.countyBindingSource.DataSource = _bindingList;
        }

        void _bindingList_AddingNew(object sender, AddingNewEventArgs e)
        {
            County county3 = new County();
            county3.CountyID = 3;
            county3.CountyName = "桃園縣";
            county3.CountyDescription = "依然買不起";

            e.NewObject = county3;
        }
    }
}

 

編譯執行後,按下bindingNavigator上的『+』按鈕看成果。

 

image

 

當沒有處理 AddingNew事件並且資料物件有預設建構函式,BindingList<T>會加入資料物件預設建構函式建立新物件。
修改本文範例為

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace WindowsFormsApplication1
{
    public class County
    {
        // Properties
        public int CountyID { get; set; }

        public string CountyName { get; set; }

        public string CountyDescription { get; set; }


        // Constructor
        public County()
        {
            this.CountyID = 4;
            this.CountyName = "新竹市";
            this.CountyDescription = "園區無敵貴";
        }
    }
}

 

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        BindingList<County> _bindingList = new BindingList<County>();

        public Form1()
        {
            InitializeComponent();

            // Add Item
            County county1 = new County();
            county1.CountyID = 1;
            county1.CountyName = "台北市";
            county1.CountyDescription = "買不起";
            _bindingList.Add(county1);

            County county2 = new County();
            county2.CountyID = 2;
            county2.CountyName = "新北市";
            county2.CountyDescription = "還是買不起";
            _bindingList.Add(county2);

            // Data Binding
            this.countyBindingSource.DataSource = _bindingList;
        }
    }
}

 

編譯執行後,按下bindingNavigator上的『+』按鈕看成果。

 

image

 

當沒有處理 AddingNew事件並且資料物件沒有預設建構函式,BindingList<T>會將自己的 AllowNew屬性設定為 false,不允許新增物件。
修改本文範例為

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace WindowsFormsApplication1
{
    public class County
    {
        // Properties
        public int CountyID { get; set; }

        public string CountyName { get; set; }

        public string CountyDescription { get; set; }


        // Constructor
        public County(int countyID)
        {
            this.CountyID = countyID;
            this.CountyName = string.Empty;
            this.CountyDescription = string.Empty;
        }
    }
}

 

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        BindingList<County> _bindingList = new BindingList<County>();

        public Form1()
        {
            InitializeComponent();

            // Add Item
            County county1 = new County(1);
            county1.CountyName = "台北市";
            county1.CountyDescription = "買不起";
            _bindingList.Add(county1);

            County county2 = new County(2);
            county2.CountyName = "新北市";
            county2.CountyDescription = "還是買不起";
            _bindingList.Add(county2);

            // Data Binding
            this.countyBindingSource.DataSource = _bindingList;
        }
    }
}

 

編譯執行後,會發現禁止按下bindingNavigator上的『+』按鈕,不允許新增。

 

image

 

相關資料 : BindingList(Of T)BindingSource.AddingNew

 

 

ITypedList :

 

Data Binding在運作的時候,會依照資料來源解析出資料物件,再將資料物件的屬性包裝成屬性物件 PropertyDescriptor。
運作模式的相關資料可以參考 : [.NET] : BindingSource使用模式 - Data Binding基礎知識 (一)

 

當開發人員要在 Data Binding時使用自訂 PropertyDescriptor來做屬性的存取顯示時,實作 ITypedList介面就可以取代預設的運作流程。
首先建立自訂 PropertyDescriptor

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;

namespace WindowsFormsApplication1
{
    public class SamplePropertyDescriptor : PropertyDescriptor
    {
        // Properties
        private readonly PropertyDescriptor _component = null;


        // Constructor
        public SamplePropertyDescriptor(PropertyDescriptor component)
            : base(component)
        {
            #region Require

            if (component == null) throw new ArgumentNullException("component");

            #endregion
            _component = component;
        }


        // Properties
        public override Type ComponentType
        {
            get
            {
                return _component.ComponentType;
            }
        }

        public override TypeConverter Converter
        {
            get
            {
                return _component.Converter;
            }
        }

        public override bool IsLocalizable
        {
            get
            {
                return _component.IsLocalizable;
            }
        }

        public override bool IsReadOnly
        {
            get
            {
                return _component.IsReadOnly;
            }
        }

        public override Type PropertyType
        {
            get
            {
                return _component.PropertyType;
            }
        }


        // Methods
        public override object GetValue(object component)
        {
            return (component as County).CountyDescription + "$$$$$$$$";
        }

        public override void SetValue(object component, object value)
        {
            _component.SetValue(component, (value as string).Replace("$$$$$$$$", string.Empty));
        }        

        public override void ResetValue(object component)
        {
            _component.ResetValue(component);
        }

        public override bool CanResetValue(object component)
        {
            return _component.CanResetValue(component);
        }

        public override bool ShouldSerializeValue(object component)
        {
            return _component.ShouldSerializeValue(component);
        }


        public override object GetEditor(Type editorBaseType)
        {
            return _component.GetEditor(editorBaseType);
        }

        public override PropertyDescriptorCollection GetChildProperties(object instance, Attribute[] filter)
        {
            return _component.GetChildProperties(instance, filter);
        }


        public override void AddValueChanged(object component, EventHandler handler)
        {
            _component.AddValueChanged(component, handler);
        }

        public override void RemoveValueChanged(object component, EventHandler handler)
        {
            _component.RemoveValueChanged(component, handler);
        }
    }
}

 

再來建立繼承 BindingList及實作 ITypedList的自訂BindingList

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;

namespace WindowsFormsApplication1
{
    public class SampleBindingList : BindingList<County>, ITypedList
    {
        public string GetListName(PropertyDescriptor[] listAccessors)
        {
            return typeof(County).Name;
        }

        public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors)
        {
            if (listAccessors != null && listAccessors.Length > 0)
            {
                throw new InvalidOperationException();
            }
            else
            {
                // Result
                List<PropertyDescriptor> propertyDescriptorCollection = new List<PropertyDescriptor>();

                // Create           
                foreach (PropertyDescriptor propertyDescriptor in TypeDescriptor.GetProperties(typeof(County)))
                {
                    if (propertyDescriptor.Name == "CountyDescription")
                    {
                        propertyDescriptorCollection.Add(new SamplePropertyDescriptor(propertyDescriptor));
                    }
                    else
                    {
                        propertyDescriptorCollection.Add(propertyDescriptor);
                    }
                }

                // Return
                return new PropertyDescriptorCollection(propertyDescriptorCollection.ToArray());
            }
        }
    }
}

 

最後修改本文範例為

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace WindowsFormsApplication1
{
    public class County
    {
        // Properties
        public int CountyID { get; set; }

        public string CountyName { get; set; }

        public string CountyDescription { get; set; }


        // Constructor
        public County()
        {
            this.CountyID = 0;
            this.CountyName = string.Empty;
            this.CountyDescription = string.Empty;
        }
    }
}

 

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        SampleBindingList _bindingList = new SampleBindingList();

        public Form1()
        {
            InitializeComponent();

            // Add Item
            County county1 = new County();
            county1.CountyID = 1;
            county1.CountyName = "台北市";
            county1.CountyDescription = "買不起";
            _bindingList.Add(county1);

            County county2 = new County();
            county2.CountyID = 2;
            county2.CountyName = "新北市";
            county2.CountyDescription = "還是買不起";
            _bindingList.Add(county2);

            // Data Binding
            this.countyBindingSource.DataSource = _bindingList;
        }
    }
}

 

完成,看成果。

 

image

 

相關資料 : ITypedListHOW TO:實作 ITypedList 介面

 

期許自己
能以更簡潔的文字與程式碼,傳達出程式設計背後的精神。
真正做到「以形寫神」的境界。