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
}
} |