Mercurial > pub > bltoolkit
diff Source/ComponentModel/BindingListImpl.cs @ 0:f990fcb411a9
Копия текущей версии из github
author | cin |
---|---|
date | Thu, 27 Mar 2014 21:46:09 +0400 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/ComponentModel/BindingListImpl.cs Thu Mar 27 21:46:09 2014 +0400 @@ -0,0 +1,876 @@ +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 + } +}