Mercurial > pub > bltoolkit
view Source/ComponentModel/BindingListImpl.cs @ 9:1e85f66cf767 default tip
update bltoolkit
author | nickolay |
---|---|
date | Thu, 05 Apr 2018 20:53:26 +0300 |
parents | f990fcb411a9 |
children |
line wrap: on
line source
using System; using System.Collections; using System.Collections.Specialized; using System.ComponentModel; using System.Diagnostics; using BLToolkit.EditableObjects; using BLToolkit.Reflection; namespace BLToolkit.ComponentModel { public class BindingListImpl: IBindingListView, ICancelAddNew, INotifyCollectionChanged { #region Init public BindingListImpl(IList list, Type itemType) { if (list == null) throw new ArgumentNullException("list"); if (itemType == null) throw new ArgumentNullException("itemType"); _list = list; _itemType = itemType; AddInternal(_list); } #endregion #region Protected Members private readonly IList _list; private readonly Type _itemType; private void ApplySort(IComparer comparer) { if (_list is ISortable) ((ISortable)_list).Sort(0, _list.Count, comparer); else if (_list is ArrayList) ((ArrayList)_list).Sort(0, _list.Count, comparer); else if (_list is Array) Array.Sort((Array)_list, comparer); else { object[] items = new object[_list.Count]; _list.CopyTo(items, 0); Array.Sort(items, comparer); for (int i = 0; i < _list.Count; i++) _list[i] = items[i]; } _isSorted = true; } #endregion #region IBindingList Members #region Command private int _newItemIndex = -1; private INotifyObjectEdit _newObject; public object AddNew() { if (AllowNew == false) throw new NotSupportedException(); EndNew(); object o = TypeAccessor.CreateInstanceEx(_itemType); _newObject = o as INotifyObjectEdit; if (_newObject != null) _newObject.ObjectEdit += NewObject_ObjectEdit; _newItemIndex = _list.Add(o); OnAddItem(o, _newItemIndex); Debug.WriteLine(string.Format("AddNew - ({0})", o.GetType().Name)); return o; } void NewObject_ObjectEdit(object sender, ObjectEditEventArgs args) { if (sender == _newObject) { switch (args.EditType) { case ObjectEditType.End: EndNew(); break; case ObjectEditType.Cancel: CancelNew(_newItemIndex); break; default: return; } } } public bool AllowNew { get { return !_list.IsFixedSize; } } public bool AllowEdit { get { return !_list.IsReadOnly; } } public bool AllowRemove { get { return !_list.IsFixedSize; } } #endregion #region Change Notification private bool _notifyChanges = true; public bool NotifyChanges { get { return _notifyChanges; } set { _notifyChanges = value; } } public bool SupportsChangeNotification { get { return true; } } public event ListChangedEventHandler ListChanged; private void FireListChangedEvent(object sender, ListChangedEventArgs e) { if (_notifyChanges && ListChanged != null) ListChanged(sender, e); } protected virtual void OnListChanged(EditableListChangedEventArgs e) { FireListChangedEvent(this, e); } protected void OnListChanged(ListChangedType listChangedType, int index) { OnListChanged(new EditableListChangedEventArgs(listChangedType, index)); } private void ItemPropertyChanged(object sender, PropertyChangedEventArgs e) { if (_notifyChanges && sender != null) { int indexOfSender = _list.IndexOf(sender); if (indexOfSender >= 0) { MemberAccessor ma = TypeAccessor.GetAccessor(sender.GetType())[e.PropertyName]; if (ma != null) OnListChanged(new EditableListChangedEventArgs(indexOfSender, ma.PropertyDescriptor)); else OnListChanged(new EditableListChangedEventArgs(ListChangedType.ItemChanged, indexOfSender)); // Do not fire an event for OnCollectionChanged here. if (_isSorted && _list.Count > 1) { int newIndex = GetItemSortedPosition(indexOfSender, sender); if (newIndex != indexOfSender) { _list.RemoveAt(indexOfSender); _list.Insert(newIndex, sender); OnMoveItem(sender, indexOfSender, newIndex); } } } } } #endregion #region Sorting public bool SupportsSorting { get { return true; } } [NonSerialized] private bool _isSorted; public bool IsSorted { get { return _isSorted; } } [NonSerialized] private PropertyDescriptor _sortProperty; public PropertyDescriptor SortProperty { get { return _sortProperty; } } [NonSerialized] private ListSortDirection _sortDirection; public ListSortDirection SortDirection { get { return _sortDirection; } } public void ApplySort(PropertyDescriptor property, ListSortDirection direction) { Debug.WriteLine(string.Format("Begin ApplySort(\"{0}\", {1})", property.Name, direction)); _sortProperty = property; _sortDirection = direction; _sortDescriptions = null; ApplySort(GetSortComparer(_sortProperty, _sortDirection)); if (_list.Count > 0) OnReset(); Debug.WriteLine(string.Format("End ApplySort(\"{0}\", {1})", property.Name, direction)); } public void RemoveSort() { _isSorted = false; _sortProperty = null; _sortDescriptions = null; OnReset(); } #endregion #region Searching public bool SupportsSearching { get { return true; } } public int Find(PropertyDescriptor property, object key) { if (property == null) throw new ArgumentException("property"); if (key != null) for (int i = 0; i < _list.Count; i++) if (key.Equals(property.GetValue(_list[i]))) return i; return -1; } #endregion #region Indexes public void AddIndex(PropertyDescriptor property) { } public void RemoveIndex(PropertyDescriptor property) { } #endregion #endregion #region ICancelAddNew Members public void CancelNew(int itemIndex) { if (itemIndex >= 0 && itemIndex == _newItemIndex) { _list.RemoveAt(itemIndex); OnRemoveItem(_newObject, itemIndex); EndNew(); } } public void EndNew(int itemIndex) { if (itemIndex == _newItemIndex) EndNew(); } public void EndNew() { _newItemIndex = -1; if (_newObject != null) _newObject.ObjectEdit -= NewObject_ObjectEdit; _newObject = null; } #endregion #region IList Members public int Add(object value) { int index; if (!_isSorted) index = _list.Add(value); else { index = GetSortedInsertIndex(value); _list.Insert(index, value); } AddInternal(value); OnAddItem(value, index); return index; } public void Clear() { if (_list.Count > 0) { RemoveInternal(_list); _list.Clear(); OnReset(); } } public bool Contains(object value) { return _list.Contains(value); } public int IndexOf(object value) { return _list.IndexOf(value); } public void Insert(int index, object value) { if (_isSorted) index = GetSortedInsertIndex(value); _list.Insert(index, value); AddInternal(value); OnAddItem(value, index); } public bool IsFixedSize { get { return _list.IsFixedSize; } } public bool IsReadOnly { get { return _list.IsReadOnly; } } public void Remove(object value) { int index = IndexOf(value); if (index >= 0) RemoveInternal(value); _list.Remove(value); if (index >= 0) OnRemoveItem(value, index); } public void RemoveAt(int index) { object value = this[index]; RemoveInternal(value); _list.RemoveAt(index); OnRemoveItem(value, index); } public object this[int index] { get { return _list[index]; } set { object o = _list[index]; if (o != value) { RemoveInternal(o); _list[index] = value; AddInternal(value); OnChangeItem(o, value, index); if (_isSorted) { int newIndex = GetItemSortedPosition(index, value); if (newIndex != index) { _list.RemoveAt(index); _list.Insert(newIndex, value); } OnMoveItem(value, index, newIndex); } } } } #endregion #region ICollection Members public void CopyTo(Array array, int index) { _list.CopyTo(array, index); } public int Count { get { return _list.Count; } } public bool IsSynchronized { get { return _list.IsSynchronized; } } public object SyncRoot { get { return _list.SyncRoot; } } #endregion #region IEnumerable Members public IEnumerator GetEnumerator() { return _list.GetEnumerator(); } #endregion #region SortPropertyComparer class SortPropertyComparer : IComparer { readonly PropertyDescriptor _property; readonly ListSortDirection _direction; public SortPropertyComparer(PropertyDescriptor property, ListSortDirection direction) { _property = property; _direction = direction; } public int Compare(object x, object y) { object a = _property.GetValue(x); object b = _property.GetValue(y); int n = Comparer.Default.Compare(a, b); return _direction == ListSortDirection.Ascending? n: -n; } } #endregion #region IComparer Accessor public IComparer GetSortComparer() { if (_isSorted) { if (_sortDescriptions != null) return GetSortComparer(_sortDescriptions); return GetSortComparer(_sortProperty, _sortDirection); } return null; } private IComparer GetSortComparer(PropertyDescriptor sortProperty, ListSortDirection sortDirection) { if (_sortSubstitutions.ContainsKey(sortProperty.Name)) sortProperty = ((SortSubstitutionPair)_sortSubstitutions[sortProperty.Name]).Substitute; return new SortPropertyComparer(sortProperty, sortDirection); } private IComparer GetSortComparer(ListSortDescriptionCollection sortDescriptions) { bool needSubstitution = false; if (_sortSubstitutions.Count > 0) { foreach (ListSortDescription sortDescription in sortDescriptions) { if (_sortSubstitutions.ContainsKey(sortDescription.PropertyDescriptor.Name)) { needSubstitution = true; break; } } if (needSubstitution) { ListSortDescription[] sorts = new ListSortDescription[sortDescriptions.Count]; sortDescriptions.CopyTo(sorts, 0); for (int i = 0; i < sorts.Length; i++) if (_sortSubstitutions.ContainsKey(sorts[i].PropertyDescriptor.Name)) sorts[i] = new ListSortDescription(((SortSubstitutionPair)_sortSubstitutions[sorts[i].PropertyDescriptor.Name]).Substitute, sorts[i].SortDirection); sortDescriptions = new ListSortDescriptionCollection(sorts); } } return new SortListPropertyComparer(sortDescriptions); } #endregion #region IBindingListView Members public bool SupportsAdvancedSorting { get { return true; } } public void ApplySort(ListSortDescriptionCollection sorts) { _sortDescriptions = sorts; _isSorted = true; _sortProperty = null; ApplySort(GetSortComparer(sorts)); if (_list.Count > 0) OnReset(); } [NonSerialized] private ListSortDescriptionCollection _sortDescriptions; public ListSortDescriptionCollection SortDescriptions { get { return _sortDescriptions; } } public bool SupportsFiltering { get { return false; } } public string Filter { get { throw new NotImplementedException("The method 'BindingListImpl.get_Filter' is not implemented."); } set { throw new NotImplementedException("The method 'BindingListImpl.set_Filter' is not implemented."); } } public void RemoveFilter() { throw new NotImplementedException("The method 'BindingListImpl.RemoveFilter()' is not implemented."); } #endregion #region SortListPropertyComparer class SortListPropertyComparer : IComparer { readonly ListSortDescriptionCollection _sorts; public SortListPropertyComparer(ListSortDescriptionCollection sorts) { _sorts = sorts; } public int Compare(object x, object y) { for (int i = 0; i < _sorts.Count; i++) { PropertyDescriptor property = _sorts[i].PropertyDescriptor; object a = property.GetValue(x); object b = property.GetValue(y); int n = Comparer.Default.Compare(a, b); if (n != 0) return _sorts[i].SortDirection == ListSortDirection.Ascending? n: -n; } return 0; } } #endregion #region Sorting enhancement private readonly Hashtable _sortSubstitutions = new Hashtable(); private class SortSubstitutionPair { public SortSubstitutionPair(PropertyDescriptor original, PropertyDescriptor substitute) { Original = original; Substitute = substitute; } public readonly PropertyDescriptor Original; public readonly PropertyDescriptor Substitute; } public void CreateSortSubstitution(string originalProperty, string substituteProperty) { TypeAccessor typeAccessor = TypeAccessor.GetAccessor(_itemType); PropertyDescriptor originalDescriptor = typeAccessor.PropertyDescriptors[originalProperty]; PropertyDescriptor substituteDescriptor = typeAccessor.PropertyDescriptors[substituteProperty]; if (originalDescriptor == null) throw new InvalidOperationException("Can not retrieve PropertyDescriptor for original property: " + originalProperty); if (substituteDescriptor == null) throw new InvalidOperationException("Can not retrieve PropertyDescriptor for substitute property: " + substituteProperty); _sortSubstitutions[originalProperty] = new SortSubstitutionPair(originalDescriptor, substituteDescriptor); } public void RemoveSortSubstitution(string originalProperty) { _sortSubstitutions.Remove(originalProperty); } #endregion #region Sort enforcement public int GetItemSortedPosition(int index, object sender) { IComparer comparer = GetSortComparer(); if (comparer == null) return index; if ((index > 0 && comparer.Compare(_list[index - 1], sender) > 0) || (index < _list.Count - 1 && comparer.Compare(_list[index + 1], sender) < 0)) { for (int i = 0; i < _list.Count; i++) { if (i != index && comparer.Compare(_list[i], sender) > 0) { if (i > index) return i - 1; return i; } } return _list.Count - 1; } return index; } public int GetSortedInsertIndex(object value) { IComparer comparer = GetSortComparer(); if (comparer == null) return -1; for (int i = 0; i < _list.Count; i++) if (comparer.Compare(_list[i], value) > 0) return i; return _list.Count; } #endregion #region Misc/Range Operations public void Move(int newIndex, int oldIndex) { if (oldIndex != newIndex) { EndNew(); object o = _list[oldIndex]; _list.RemoveAt(oldIndex); if (!_isSorted) _list.Insert(newIndex, o); else _list.Insert(newIndex = GetSortedInsertIndex(o), o); OnMoveItem(o, oldIndex, newIndex); } } public void AddRange(ICollection c) { foreach (object o in c) { if (!_isSorted) _list.Add(o); else _list.Insert(GetSortedInsertIndex(o), o); } AddInternal(c); OnReset(); } public void InsertRange(int index, ICollection c) { if (c.Count == 0) return; foreach (object o in c) { if (!_isSorted) _list.Insert(index++, o); else _list.Insert(GetSortedInsertIndex(o), o); } AddInternal(c); OnReset(); } public void RemoveRange(int index, int count) { object[] toRemove = new object[count]; for (int i = index; i < _list.Count && i < index + count; i++) toRemove[i - index] = _list[i]; RemoveInternal(toRemove); foreach (object o in toRemove) _list.Remove(o); OnReset(); } public void SetRange(int index, ICollection c) { int cCount = c.Count; if (index < 0 || index >= _list.Count - cCount) throw new ArgumentOutOfRangeException("index"); bool oldNotifyChanges = _notifyChanges; _notifyChanges = false; int i = index; foreach (object newObject in c) { RemoveInternal(_list[i + index]); _list[i + index] = newObject; } AddInternal(c); if (_isSorted) ApplySort(GetSortComparer()); _notifyChanges = oldNotifyChanges; OnReset(); } #endregion #region Add/Remove Internal private void AddInternal(object value) { EndNew(); if (value is INotifyPropertyChanged) ((INotifyPropertyChanged)value).PropertyChanged += ItemPropertyChanged; } private void RemoveInternal(object value) { EndNew(); if (value is INotifyPropertyChanged) ((INotifyPropertyChanged)value).PropertyChanged -= ItemPropertyChanged; } private void AddInternal(IEnumerable e) { foreach (object o in e) AddInternal(o); } private void RemoveInternal(IEnumerable e) { foreach (object o in e) RemoveInternal(o); } private void OnAddItem(object item, int index) { OnListChanged(new EditableListChangedEventArgs(ListChangedType.ItemAdded, index)); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index)); } private void OnRemoveItem(object item, int index) { OnListChanged(new EditableListChangedEventArgs(ListChangedType.ItemDeleted, index)); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index)); } private void OnMoveItem(object item, int oldIndex, int newIndex) { OnListChanged(new EditableListChangedEventArgs(newIndex, oldIndex)); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Move, item, newIndex, oldIndex)); } private void OnChangeItem(object oldValue, object newValue, int index) { OnListChanged(new EditableListChangedEventArgs(ListChangedType.ItemChanged, index)); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, oldValue, newValue, index)); } private void OnReset() { OnListChanged(new EditableListChangedEventArgs(ListChangedType.Reset)); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } #endregion #region INotifyCollectionChanged Members public event NotifyCollectionChangedEventHandler CollectionChanged; private void FireCollectionChangedEvent(object sender, NotifyCollectionChangedEventArgs ea) { if (_notifyChanges && CollectionChanged != null) CollectionChanged(sender, ea); } protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs ea) { FireCollectionChangedEvent(this, ea); } #endregion } }