• [Windows Forms] : BindingSource使用模式 加强版BindingList<T>


    前言 :

    一般使用 BindingSource做 Data Binding的工作,不管是用 ADO.NET对象或是自定义数据对象当作数据源。
    运作流程大多类似
    1.读取数据并将数据填写进 DataSet(or BindingList)
    2.将DataSet(or BindingList)系结至BindingSource
    3.画面Control触发事件时,操作数据库(or 集合)变更数据,并且操作BindingSource显示数据。

    这样的运作流程,因为靠画面Control触发的事件,来当作操作函式的进入点。
    把这样的软件架构,会显得各层之间的职责略显模糊。

    职责模糊范例程序 : 按此下载

    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;
    using System.Data;
    using System.Data.SqlClient;
    
    namespace WindowsFormsApplication1
    {
        public partial class Form1 : Form
        {       
            public Form1()
            {
                InitializeComponent();
    
                // Fill
                BindingList<County> bindingList = new BindingList<County>();
                foreach (County county in this.GetList())
                {
                    bindingList.Add(county);
                }
    
                // Binding
                countyBindingSource.DataSource = bindingList;
            }
            
            private void bindingNavigatorAddNewItem_Click(object sender, EventArgs e)
            {
                // Operate 
                County item = countyBindingSource.Current as County;
                if (item != null)
                {
                    this.Add(item);
    
                    MessageBox.Show("Database Added");
                }
            }
    
    
            private const string _connectionString = @"Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\Database1.mdf;Integrated Security=True;User Instance=True";
    
            public void Add(County item)
            {
                #region Require
    
                if (item == null) throw new ArgumentNullException();
    
                #endregion
                SqlCommand command;
                using (SqlConnection connection = new SqlConnection(_connectionString))
                {
                    // Connection
                    connection.Open();
    
                    // Insert County
                    using (command = connection.CreateCommand())
                    {
                        command.CommandType = CommandType.Text;
                        command.CommandText = "INSERT INTO [CountyTable](CountyID, CountyName, CountyDescription) VALUES (@CountyID, @CountyName, @CountyDescription)";
                        command.Parameters.AddWithValue("@CountyID", item.CountyID);
                        command.Parameters.AddWithValue("@CountyName", item.CountyName);
                        command.Parameters.AddWithValue("@CountyDescription", item.CountyDescription);
                        command.ExecuteNonQuery();
                    }
                }
            }
    
            public IEnumerable<County> GetList()
            {
                SqlCommand command;
                using (SqlConnection connection = new SqlConnection(_connectionString))
                {
                    // Connection
                    connection.Open();
    
                    // Result
                    List<County> itemCollection = new List<County>();
    
                    // Select County          
                    using (command = connection.CreateCommand())
                    {
                        command.CommandType = CommandType.Text;
                        command.CommandText = "SELECT * FROM [CountyTable]";
                        using (SqlDataReader dataReader = command.ExecuteReader())
                        {
                            while (dataReader.Read())
                            {
                                County item = new County();
                                item.CountyID = Convert.ToInt32(dataReader["CountyID"]);
                                item.CountyName = Convert.ToString(dataReader["CountyName"]);
                                item.CountyDescription = Convert.ToString(dataReader["CountyDescription"]);
                                itemCollection.Add(item);
                            }
                        }
                    }
    
                    // Return 
                    return itemCollection;
                }
            }
        }
    }
    

    本篇文章介绍如何开发加强版BindingList<T>,用来将 Data Binding的运作流程作封装。
    将原本各层之间模糊不清的职责,做一定程度的分派。
    让开发人员在设计 Data Binding相关程序代码时,能将焦点集中在数据对象的操作工作上。

    相关资料 :
    [.NET] : BindingSource使用模式 - Data Binding基础知识 (一)
    [.NET] : BindingSource使用模式 - Data Binding基础知识 (二)

    实作 :

    首先看看开发人员如何使用加强版BindingList<T>完成工作。
    主要使用的接口及对象为
    •StandardBindingList<T>类别,将Data Binding的运作流程封装在内,用来取代 .NET内建提供的 System.ComponentModel.BindingList<T>。
    •IStandardBindingListStrategy<T>接口,开发人员实作 IStandardBindingListStrategy<T>并且注入后,就完成 Data Binding的数据源的开发工作。

    加强版BindingList<T>范例程序 : 按此下载

    先展示实际使用的程序代码及成果。
    可以看到开发人员,只需要建立跟数据库沟通的对象,就可以完成画面到数据库一连串的开发工作。

    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;
    using CLK.ComponentModel;
    
    namespace StandardBindingListSample
    {
        public partial class Form1 : Form
        {
            // Properties
            private const string _connectionString = @"Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\CountyBindingListStrategyDatabase.mdf;Integrated Security=True;User Instance=True";
                
            // Constructor
            public Form1()
            {
                InitializeComponent();
    
                this.countyBindingSource.DataSource = new StandardBindingList<County>(new SqlCountyBindingListStrategy(_connectionString));   
            }
        }
    }
    using System;
    using System.Collections.Generic;
    using System.Data;
    using System.Data.SqlClient;
    using CLK.ComponentModel;
    
    namespace StandardBindingListSample
    {
        public class SqlCountyBindingListStrategy : IStandardBindingListStrategy<County>
        {
            // Properties
            private readonly string _connectionString = string.Empty;
    
    
            // Constructor
            public SqlCountyBindingListStrategy(string connectionString)
            {
                #region Require
    
                if (string.IsNullOrEmpty(connectionString) == true) throw new ArgumentNullException();
    
                #endregion
                _connectionString = connectionString;
            }
    
    
            // Methods
            private SqlConnection CreateConnection()
            {
                return new SqlConnection(_connectionString);
            }
            
    
            public void Add(County item)
            {
                #region Require
    
                if (item == null) throw new ArgumentNullException();
    
                #endregion
                SqlCommand command;
                using (SqlConnection connection = this.CreateConnection())
                {
                    // Connection
                    connection.Open();
    
                    // Insert County
                    using (command = connection.CreateCommand())
                    {
                        command.CommandType = CommandType.Text;
                        command.CommandText = "INSERT INTO [CountyTable](CountyID, CountyName, CountyDescription) VALUES (@CountyID, @CountyName, @CountyDescription)";
                        command.Parameters.AddWithValue("@CountyID", item.CountyID);
                        command.Parameters.AddWithValue("@CountyName", item.CountyName);
                        command.Parameters.AddWithValue("@CountyDescription", item.CountyDescription);
                        command.ExecuteNonQuery();
                    }
                }
            }
    
            public void Modify(County item)
            {
                #region Require
    
                if (item == null) throw new ArgumentNullException();
    
                #endregion
                SqlCommand command;
                using (SqlConnection connection = this.CreateConnection())
                {
                    // Connection
                    connection.Open();
    
                    // Update County          
                    using (command = connection.CreateCommand())
                    {
                        command.CommandType = CommandType.Text;
                        command.CommandText = "UPDATE [CountyTable] SET CountyName=@CountyName, CountyDescription=@CountyDescription WHERE CountyID=@CountyID";
                        command.Parameters.AddWithValue("@CountyID", item.CountyID);
                        command.Parameters.AddWithValue("@CountyName", item.CountyName);
                        command.Parameters.AddWithValue("@CountyDescription", item.CountyDescription);
                        command.ExecuteNonQuery();
                    }
                }
            }
    
            public void Remove(County item)
            {
                #region Require
    
                if (item == null) throw new ArgumentNullException();
    
                #endregion
                SqlCommand command;
                using (SqlConnection connection = this.CreateConnection())
                {
                    // Connection
                    connection.Open();
    
                    // Delete County          
                    using (command = connection.CreateCommand())
                    {
                        command.CommandType = CommandType.Text;
                        command.CommandText = "DELETE FROM [CountyTable] WHERE CountyID=@CountyID";
                        command.Parameters.AddWithValue("@CountyID", item.CountyID);
                        command.ExecuteNonQuery();
                    }
                }
            }
    
            public IEnumerable<County> GetList()
            {
                SqlCommand command;
                using (SqlConnection connection = this.CreateConnection())
                {
                    // Connection
                    connection.Open();
    
                    // Result
                    List<County> itemCollection = new List<County>();
    
                    // Select County          
                    using (command = connection.CreateCommand())
                    {
                        command.CommandType = CommandType.Text;
                        command.CommandText = "SELECT * FROM [CountyTable]";
                        using (SqlDataReader dataReader = command.ExecuteReader())
                        {
                            while (dataReader.Read())
                            {
                                County item = new County();
                                item.CountyID = Convert.ToInt32(dataReader["CountyID"]);
                                item.CountyName = Convert.ToString(dataReader["CountyName"]);
                                item.CountyDescription = Convert.ToString(dataReader["CountyDescription"]);
                                itemCollection.Add(item);
                            }
                        }
                    }
    
                    // Return 
                    return itemCollection;
                }
            }
        }
    }
    

    image

    再来展开StandardBindingList的软件架构图,并且建立架构图里的类别。

    image

    首先是 StandardBindingObject<T>类别。
    -StandardBindingObject<T>封装实际要做 Data Binding的数据对象 T,让后续的程序代码能够取得数据对象 T。
    -StandardBindingObject<T>提供多个属性,让 StandardBindingList<T>将发生过的 Data Binding流程做纪录。
    -StandardBindingObject<T>也聆听,有实做INotifyPropertyChanged的数据对象 T,用来记录数据变更的流程。

    public class StandardBindingObject<T>
        where T : class, new()
    {
        // Properties
        private PropertyChangedEventHandler PropertyChangedDelegate { get; set; }
    
        public T NativeBindingObject { get; private set; }
    
        public bool IsEmptyTrack
        {
            get
            {
                if (this.IsDirty == true) return false;
                if (this.IsInsertItem == true) return false;
                if (this.IsRemoveItem == true) return false;
                if (this.IsSetItem == true) return false;
                if (this.IsClearItems == true) return false;
                if (this.IsCancelNew == true) return false;
                if (this.IsEndNew == true) return false;
                return true;
            }
        }
    
    
        public bool IsDirty { get; set; }
    
        public bool IsInsertItem { get; set; }
    
        public bool IsRemoveItem { get; set; }
    
        public bool IsSetItem { get; set; }
    
        public bool IsClearItems { get; set; }
    
        public bool IsCancelNew { get; set; }
    
        public bool IsEndNew { get; set; }
    
    
        // Constructor
        public StandardBindingObject() : this(new T()) { }
    
        public StandardBindingObject(T nativeBindingObject)
        {
            #region Require
    
            if (nativeBindingObject == null) throw new ArgumentNullException("nativeBindingObject");
    
            #endregion
    
            // Properties
            this.PropertyChangedDelegate = delegate(object sender, PropertyChangedEventArgs e) { this.IsDirty = true; };
            this.NativeBindingObject = nativeBindingObject;
            this.ResetTrack();
        }
    
    
        // Methods
        public void ResetTrack()
        {
            this.IsDirty = false;
            this.IsInsertItem = false;
            this.IsRemoveItem = false;
            this.IsSetItem = false;
            this.IsClearItems = false;
            this.IsCancelNew = false;
            this.IsEndNew = false;
        }
    
        public void HookPropertyChanged()
        {
            // INotifyPropertyChanged
            INotifyPropertyChanged notifyPropertyChanged = this.NativeBindingObject as INotifyPropertyChanged;
            if (notifyPropertyChanged != null) notifyPropertyChanged.PropertyChanged += this.PropertyChangedDelegate;
        }
    
        public void UnhookPropertyChanged()
        {
            // INotifyPropertyChanged
            INotifyPropertyChanged notifyPropertyChanged = this.NativeBindingObject as INotifyPropertyChanged;
            if (notifyPropertyChanged != null) notifyPropertyChanged.PropertyChanged -= this.PropertyChangedDelegate;
        }
    }

    再来是 StandardBindingPropertyDescriptor<T>类别。
    -StandardBindingPropertyDescriptor<T>封装实际要做 Data Binding的数据对象 T的属性对象 PropertyDescriptor,让自己对外表现的就跟被封装的对象一样。
    -StandardBindingPropertyDescriptor<T>内部存取数据对象的属性时,是读取 StandardBindingObject<T>封装的数据对象 T的属性。
    -StandardBindingPropertyDescriptor<T>透过 PropertyDescriptor的机制,聆听没有实做INotifyPropertyChanged的数据对象 T的属性数据变更,并用StandardBindingObject<T>来记录变化。

    public sealed class StandardBindingPropertyDescriptor<T> : PropertyDescriptor
        where T : class, new()
    {
        // Properties
        private readonly PropertyDescriptor _nativeBindingPropertyDescriptor = null;
    
        private readonly bool _raiseStandardBindingObjectSetDirty = false;
    
    
        // Constructor
        public StandardBindingPropertyDescriptor(PropertyDescriptor nativeBindingPropertyDescriptor)
            : base(nativeBindingPropertyDescriptor)
        {
            #region Require
    
            if (nativeBindingPropertyDescriptor == null) throw new ArgumentNullException("component");
    
            #endregion
            _nativeBindingPropertyDescriptor = nativeBindingPropertyDescriptor;
            if (typeof(INotifyPropertyChanged).IsAssignableFrom(typeof(T)) == false) _raiseStandardBindingObjectSetDirty = true;
        }
    
    
        // Properties
        public override Type ComponentType
        {
            get
            {
                return _nativeBindingPropertyDescriptor.ComponentType;
            }
        }
    
        public override TypeConverter Converter
        {
            get
            {
                return _nativeBindingPropertyDescriptor.Converter;
            }
        }
    
        public override bool IsLocalizable
        {
            get
            {
                return _nativeBindingPropertyDescriptor.IsLocalizable;
            }
        }
    
        public override bool IsReadOnly
        {
            get
            {
                return _nativeBindingPropertyDescriptor.IsReadOnly;
            }
        }
    
        public override Type PropertyType
        {
            get
            {
                return _nativeBindingPropertyDescriptor.PropertyType;
            }
        }
    
    
        // Methods
        private void GetBindingObject(object component, out StandardBindingObject<T> standardBindingObject, out T nativeBindingObject)
        {
            #region Require
    
            if (component == null) throw new ArgumentNullException("component");
    
            #endregion
    
            // StandardBindingObject
            standardBindingObject = component as StandardBindingObject<T>;
            if (standardBindingObject == null) throw new ArgumentNullException("standardBindingObject");
    
            // NativeBindingObject
            nativeBindingObject = standardBindingObject.NativeBindingObject;
            if (nativeBindingObject == null) throw new ArgumentNullException("nativeBindingObject");
        }
    
        private StandardBindingObject<T> GetStandardBindingObject(object component)
        {
            #region Require
    
            if (component == null) throw new ArgumentNullException("component");
    
            #endregion
    
            // GetBindingObject
            StandardBindingObject<T> standardBindingObject = null;
            T nativeBindingObject = null;
            this.GetBindingObject(component, out standardBindingObject, out nativeBindingObject);
    
            // Return
            return standardBindingObject;
        }
    
        private T GetNativeBindingObject(object component)
        {
            #region Require
    
            if (component == null) throw new ArgumentNullException("component");
    
            #endregion
    
            // GetBindingObject
            StandardBindingObject<T> standardBindingObject = null;
            T nativeBindingObject = null;
            this.GetBindingObject(component, out standardBindingObject, out nativeBindingObject);
    
            // Return
            return nativeBindingObject;
        }
    
    
        public override void SetValue(object component, object value)
        {
            // GetBindingObject
            StandardBindingObject<T> standardBindingObject = null;
            T nativeBindingObject = null;
            this.GetBindingObject(component, out standardBindingObject, out nativeBindingObject);
    
            // RaiseStandardBindingObjectSetDirty
            if (_raiseStandardBindingObjectSetDirty == false)
            {
                // SetValue
                _nativeBindingPropertyDescriptor.SetValue(nativeBindingObject, value);
            }
            else
            {
                // SetDirty
                EventHandler setDirtyDelegate = delegate(object sender, EventArgs e)
                {
                    standardBindingObject.IsDirty = true;
                };
    
                // SetValue
                _nativeBindingPropertyDescriptor.AddValueChanged(nativeBindingObject, setDirtyDelegate);
                _nativeBindingPropertyDescriptor.SetValue(nativeBindingObject, value);
                _nativeBindingPropertyDescriptor.RemoveValueChanged(nativeBindingObject, setDirtyDelegate);
            }
        }
    
        public override object GetValue(object component)
        {
            return _nativeBindingPropertyDescriptor.GetValue(this.GetNativeBindingObject(component));
        }
    
        public override void ResetValue(object component)
        {
            _nativeBindingPropertyDescriptor.ResetValue(this.GetNativeBindingObject(component));
        }
    
        public override bool CanResetValue(object component)
        {
            return _nativeBindingPropertyDescriptor.CanResetValue(this.GetNativeBindingObject(component));
        }
    
        public override bool ShouldSerializeValue(object component)
        {
            return _nativeBindingPropertyDescriptor.ShouldSerializeValue(this.GetNativeBindingObject(component));
        }
    
    
        public override object GetEditor(Type editorBaseType)
        {
            return _nativeBindingPropertyDescriptor.GetEditor(editorBaseType);
        }
    
        public override PropertyDescriptorCollection GetChildProperties(object instance, Attribute[] filter)
        {
            return _nativeBindingPropertyDescriptor.GetChildProperties(this.GetNativeBindingObject(instance), filter);
        }
    
    
        public override void AddValueChanged(object component, EventHandler handler)
        {
            _nativeBindingPropertyDescriptor.AddValueChanged(this.GetNativeBindingObject(component), handler);
        }
    
        public override void RemoveValueChanged(object component, EventHandler handler)
        {
            _nativeBindingPropertyDescriptor.RemoveValueChanged(this.GetNativeBindingObject(component), handler);
        }
    }

    再来定义 IStandardBindingListStrategy<T>界面。
    -IStandardBindingListStrategy<T>定义,要做 Data Binding的数据对象 T,进出系统边界应该要实作的功能。

    public interface IStandardBindingListStrategy<T>
        where T : class, new()
    {
        void Add(T item);
    
        void Modify(T item);
    
        void Remove(T item);       
    
        IEnumerable<T> GetList();
    }

    最后是StandardBindingList<T>类别。
    -StandardBindingList<T>将Data Binding的运作流程封装在内,用来取代 .NET内建提供的 System.ComponentModel.BindingList<T>。
    -StandardBindingList<T>透过继承 Override的方式来聆听发生过的 Data Binding流程,使用 StandardBindingObject<T>来记录变化。
    -StandardBindingList<T>实作了ITypedList接口,取代原本 Data Binding流程里取得PropertyDescriptor的流程。将原本应该取得数据对象T的属性对象,改为取得StandardBindingPropertyDescriptor<T>。
    -StandardBindingList<T>开放了Refresh()函式,执行这个函式StandardBindingList<T>就会透过IStandardBindingListStrategy<T>取得数据对象 T的数据做 Data Binding的动作。
    -StandardBindingList<T>会在每个 Data Binding流程里,检查StandardBindingObject<T>发生过的纪录。当记录满足条件,就会呼叫IStandardBindingListStrategy<T>的函式处理数据对象 T。

    public class StandardBindingList<T> : BindingList<StandardBindingObject<T>>, ITypedList
        where T : class, new()
    {
        // Properties       
        private readonly IStandardBindingListStrategy<T> _strategy = null;
        
        private readonly PropertyDescriptorCollection _propertyDescriptorCollection = null;
    
        private bool _isRefreshing = false;
    
    
        // Constructor
        public StandardBindingList(IStandardBindingListStrategy<T> strategy) : this(strategy, true) { }
    
        public StandardBindingList(IStandardBindingListStrategy<T> strategy, bool runRefresh)
        {
            #region Require
    
            if (strategy == null) throw new ArgumentNullException();
    
            #endregion
    
            // Properties 
            _strategy = strategy;
            _propertyDescriptorCollection = this.CreateStandardBindingPropertyDescriptorCollection();
    
            // Refresh
            if (runRefresh == true)
            {
                this.Refresh();
            }
        }
    
    
        // Methods        
        private PropertyDescriptorCollection CreateStandardBindingPropertyDescriptorCollection()
        {
            // Result
            List<PropertyDescriptor> standardBindingPropertyDescriptorCollection = new List<PropertyDescriptor>();
    
            // Create           
            foreach (PropertyDescriptor nativePropertyDescriptor in TypeDescriptor.GetProperties(typeof(T)))
            {
                standardBindingPropertyDescriptorCollection.Add(new StandardBindingPropertyDescriptor<T>(nativePropertyDescriptor));
            }
    
            // Return
            return new PropertyDescriptorCollection(standardBindingPropertyDescriptorCollection.ToArray());
        }
        
        private void CommitTrack(StandardBindingObject<T> standardBindingObject)
        {
            #region Require
    
            if (standardBindingObject == null) throw new ArgumentNullException("standardBindingObject");
    
            #endregion
            if (_isRefreshing == false)
            {
                if (standardBindingObject.IsEmptyTrack == false)
                {
                    if (this.CommitTrack(standardBindingObject, _strategy) == true)
                    {
                        standardBindingObject.ResetTrack();
                    }
                }
            }
        }
    
        protected virtual bool CommitTrack(StandardBindingObject<T> standardBindingObject, IStandardBindingListStrategy<T> strategy)
        {
            #region Require
    
            if (standardBindingObject == null) throw new ArgumentNullException("standardBindingObject");
            if (strategy == null) throw new ArgumentNullException("strategy");
    
            #endregion
                        
            // Add
            if (standardBindingObject.IsInsertItem == true)
            {
                if (standardBindingObject.IsRemoveItem == false)
                {
                    if (standardBindingObject.IsCancelNew == false)
                    {
                        if (standardBindingObject.IsEndNew == true)
                        {
                            strategy.Add(standardBindingObject.NativeBindingObject);
                            return true;
                        }
                    }
                }
            }
    
            // Remove
            if (standardBindingObject.IsInsertItem == false)
            {
                if (standardBindingObject.IsCancelNew == false)
                {
                    if (standardBindingObject.IsRemoveItem == true)
                    {
                        strategy.Remove(standardBindingObject.NativeBindingObject);
                        return true;
                    }
                }
            }     
    
            // Modify
            if (standardBindingObject.IsInsertItem == false)
            {
                if (standardBindingObject.IsRemoveItem == false)
                {
                    if (standardBindingObject.IsCancelNew == false)
                    {
                        if (standardBindingObject.IsEndNew == true)
                        {
                            if (standardBindingObject.IsDirty == true)
                            {
                                strategy.Modify(standardBindingObject.NativeBindingObject);
                                return true;
                            }
                        }
                    }
                }
            }                
    
            // Return
            return false;
        }
        
    
        public void Refresh()
        {
            try
            {
                // BeginRefresh
                _isRefreshing = true;
    
                // Clear
                this.Clear();
    
                // Add
                foreach (T item in _strategy.GetList())
                {
                    StandardBindingObject<T> standardBindingObject = new StandardBindingObject<T>(item);
                    this.Add(standardBindingObject);
                    standardBindingObject.ResetTrack();
                }
            }
            finally
            {
                // EndRefresh
                _isRefreshing = false;
            }
    
            // ResetBindings
            this.ResetBindings();
        }
    
    
        #region BindingList<T>
    
        protected override void OnListChanged(ListChangedEventArgs e)
        {
            #region Require
    
            if (e == null) throw new ArgumentNullException("e");
    
            #endregion
            if (_isRefreshing == false)
            {
                base.OnListChanged(e);
            }
        }
    
        #endregion
    
        #region Collection<T>
    
        protected override void InsertItem(int index, StandardBindingObject<T> item)
        {
            #region Require
    
            if (item == null) throw new ArgumentNullException("item");
    
            #endregion
    
            // Base
            base.InsertItem(index, item);
    
            // NewItem
            item.HookPropertyChanged();
            item.IsInsertItem = true;
            this.CommitTrack(item);
        }
    
        protected override void SetItem(int index, StandardBindingObject<T> item)
        {
            #region Require
    
            if (item == null) throw new ArgumentNullException("item");
    
            #endregion
    
            // OldItem
            StandardBindingObject<T> oldItem = this[index];
            oldItem.UnhookPropertyChanged();
    
            // Base
            base.SetItem(index, item);
    
            // NewItem
            item.HookPropertyChanged();
            item.IsSetItem = true;
            this.CommitTrack(item);
        }
    
        protected override void RemoveItem(int index)
        {
            // OldItem
            StandardBindingObject<T> oldItem = this[index];
            oldItem.UnhookPropertyChanged();
            oldItem.IsRemoveItem = true;
            this.CommitTrack(oldItem);
    
            // Base
            base.RemoveItem(index);
        }
    
        protected override void ClearItems()
        {
            // OldItem
            foreach (StandardBindingObject<T> oldItem in this.Items.ToArray())
            {
                oldItem.UnhookPropertyChanged();
                oldItem.IsClearItems = true;
                this.CommitTrack(oldItem);
            }
    
            // Base
            base.ClearItems();
        }
    
        #endregion
    
        #region ICancelAddNew
    
        public override void CancelNew(int itemIndex)
        {
            // StandardBindingObject
            if (0 <= itemIndex && itemIndex < this.Count)
            {
                StandardBindingObject<T> standardBindingObject = this[itemIndex];
                standardBindingObject.IsCancelNew = true;
                this.CommitTrack(standardBindingObject);
            }
    
            // Base
            base.CancelNew(itemIndex);
        }
    
        public override void EndNew(int itemIndex)
        {
            // StandardBindingObject
            if (0 <= itemIndex && itemIndex < this.Count)
            {
                StandardBindingObject<T> standardBindingObject = this[itemIndex];
                standardBindingObject.IsEndNew = true;
                this.CommitTrack(standardBindingObject);
            }
    
            // Base
            base.EndNew(itemIndex);
        }
    
        #endregion
    
        #region ITypedList
    
        public string GetListName(PropertyDescriptor[] listAccessors)
        {
            return typeof(StandardBindingObject<T>).Name;
        }
    
        public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors)
        {
            if (listAccessors != null && listAccessors.Length > 0)
            {
                throw new InvalidOperationException();
                // return this.CreateStandardBindingPropertyDescriptorCollection(ListBindingHelper.GetListItemProperties(listAccessors[0].PropertyType));
            }
            else
            {
                return _propertyDescriptorCollection;
            }
        }
    
        #endregion        
    }
    

    后记 :

    StandardBindingList还有很多地方需要加工,例如 : 加入数据验证、或是将 CommitTrack扩充更完整。
    这些功能将会在后续的文章内一一实作,不过都还是以本章节的思路来做扩充。

  • 相关阅读:
    0005 数组(array)的静态和动态声明、最大值最小值、数组的反转、复制
    0004day04_15循环结构-循环嵌套、break和continue与标签随机数的另外一种方式 break、continue、求最大公约数、循环嵌套、求水仙花数 out标签等
    0003java.util.Scanner、输出语句、分支结构 if else 随机数 switch case选择结构和equals
    Maven配置阿里镜像
    中国大学MOOC-陈越、何钦铭-数据结构-2020春——最大子列和问题Java实现代码
    IDEA中jsp下out.println标红处理方法
    log4j:WARN No appenders could be found for logger (org.apache.ibatis.logging.LogFactory).
    IDEA下Java项目中创建xml文件
    Eclipse中web项目的导出和导入操作步骤
    EL(Expression Language)表达式语言理解
  • 原文地址:https://www.cnblogs.com/clark159/p/2205156.html
Copyright © 2020-2023  润新知