using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Linq; namespace miew.Binding { public interface IListTransactor<T> { void Insert(int index, T item); int Count { get; } int IndexOf(T item); void RemoveAt(int index); void Clear(); T Get(int index); void Set(int index, T t); T New(); }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// /// grouping of interfaces that enables maximum functionality of a bound datagrid /// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public interface IListItemBind<T> : IList<T>, IList, IBindingList, ICancelAddNew, IRaiseItemChangedEvents { }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// /// BindingListHelper(T) : support rich in-situ editing of data sources by intermediating on-the-fly between a /// ListTransactor(T) and the several interfaces which consumers may use for editing. /// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public class ListItemBindHelper<T> : IListItemBind<T> { readonly IListTransactor<T> m_lt; readonly Object sync_root = new Object(); readonly bool f_allow_new; readonly bool f_fixed_size = false; /// a singleton pending item T adding = default(T); /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// /// Constructors /// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public ListItemBindHelper(IList<T> lt, Func<T> create_new) : this(new ListAdapter(lt, create_new)) { this.f_allow_new = (create_new != null); this.f_fixed_size = lt is T[] || lt is System.Array; } public ListItemBindHelper(IEnumerable<T> lt) : this(lt.ToArray(), null) { } public ListItemBindHelper(IListTransactor<T> lt) { this.m_lt = lt; this.f_allow_new = true; if (typeof(INotifyPropertyChanged).IsAssignableFrom(typeof(T))) { pceh = new PropertyChangedEventHandler(ItemPropertyChanged); pdc = TypeDescriptor.GetProperties(typeof(T)); foreach (T t in this) AttachPropertyChanged(t); } } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// /// Item change notification /// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// PropertyChangedEventHandler pceh = null; PropertyDescriptorCollection pdc = null; int ix_mru = -1; void AttachPropertyChanged(T item) { if (pceh != null) { INotifyPropertyChanged inpc = item as INotifyPropertyChanged; if (inpc != null) inpc.PropertyChanged += pceh; } } void DetachPropertyChanged(T item) { if (pceh != null) { INotifyPropertyChanged inpc = (item as INotifyPropertyChanged); if (inpc != null) inpc.PropertyChanged -= pceh; } } void ItemPropertyChanged(object sender, PropertyChangedEventArgs e) { if (sender == null || e == null || string.IsNullOrEmpty(e.PropertyName) || !(sender is T)) { _raise_reset_event(); return; } T item = (T)sender; if (ix_mru < 0 || ix_mru >= m_lt.Count || !Object.Equals(m_lt.Get(ix_mru), item)) ix_mru = m_lt.IndexOf(item); if (ix_mru == -1) { DetachPropertyChanged(item); _raise_reset_event(); return; } PropertyDescriptor pd = pdc.Find(e.PropertyName, true); _raise_changed_event(new ListChangedEventArgs(ListChangedType.ItemChanged, ix_mru, pd)); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// /// internal /// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void _insert(int index, T item) { EndNew(0); if (f_fixed_size) return; m_lt.Insert(index, item); AttachPropertyChanged(item); _raise_changed_event(new ListChangedEventArgs(ListChangedType.ItemAdded, index)); } int _index_of(T item) { #if true return m_lt.IndexOf(item); #else int ix = m_lt.IndexOf(item); if (ix == -1 && !Object.Equals(adding, default(T)) && Object.Equals(adding, item)) ix = m_lt.Count; return ix; #endif } void _remove_at(int index) { EndNew(0); if (f_fixed_size) return; if (pceh != null) DetachPropertyChanged(_get(index)); m_lt.RemoveAt(index); _raise_changed_event(new ListChangedEventArgs(ListChangedType.ItemDeleted, index)); } T _get(int index) { return m_lt.Get(index); } void _set(int index, T value) { // EndNew(0) ??? if (pceh != null) DetachPropertyChanged(_get(index)); m_lt.Set(index, value); AttachPropertyChanged(value); _raise_changed_event(new ListChangedEventArgs(ListChangedType.ItemChanged, index)); } void _clear() { EndNew(0); if (f_fixed_size) return; foreach (T t in this) DetachPropertyChanged(t); m_lt.Clear(); _raise_reset_event(); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// /// IList(T) /// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public void Add(T item) { _insert(m_lt.Count, item); } public void Insert(int index, T item) { _insert(index, item); } public T this[int index] { get { return _get(index); } set { _set(index, (T)value); } } public bool Contains(T item) { return _index_of(item) != -1; } public bool Remove(T item) { int ix = m_lt.IndexOf(item); if (ix == -1 && !Object.Equals(adding, default(T)) && Object.Equals(adding, item)) EndNew(0); else _remove_at(ix); return true; } public void Clear() { _clear(); } public int IndexOf(T item) { return _index_of(item); } public void RemoveAt(int index) { _remove_at(index); } public int Count { get { #if true return m_lt.Count; #else int c = m_lt.Count; if (!Object.Equals(adding,default(T))) c++; return c; #endif } } public bool IsReadOnly { get { return false; } } public void CopyTo(T[] array, int index) { foreach (T t in this) array[index++] = t; } public IEnumerator<T> GetEnumerator() { int c = m_lt.Count; for (int i = 0; i < c; i++) yield return _get(i); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// /// IList /// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public int Add(object value) { if (!(value is T)) return -1; int c = m_lt.Count; _insert(c, (T)value); return c; } object IList.this[int index] { get { return _get(index); } set { _set(index, (T)value); } } public bool Contains(object value) { return value is T ? _index_of((T)value) != -1 : false; } public int IndexOf(object value) { return value is T ? _index_of((T)value) : -1; } public void Insert(int index, object value) { if (value is T) _insert(index, (T)value); } public void Remove(object value) { int ix = m_lt.IndexOf((T)value); if (ix == -1 && !Object.Equals(adding, default(T)) && Object.Equals(adding, value)) EndNew(0); else _remove_at(ix); } public bool IsFixedSize { get { return f_fixed_size; } } public void CopyTo(System.Array array, int index) { Object[] rgo = (Object[])array; foreach (T t in this) rgo[index++] = t; } public bool IsSynchronized { get { return false; } } public object SyncRoot { get { return sync_root; } } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// /// IRaiseItemChangedEvents /// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public bool RaisesItemChangedEvents { get { return this.pceh != null; } } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// /// IBindingList event /// /// Reset Much of the list has changed. Any listening controls should refresh all their data from the list. /// ItemAdded An item added to the list. NewIndex contains the index of the item that was added. /// ItemDeleted An item deleted from the list. NewIndex contains the index of the item that was deleted. /// ItemMoved An item moved within the list. /// ItemChanged An item changed in the list. NewIndex contains the index of the item that was changed. /// PropertyDescriptorAdded A PropertyDescriptor was added, which changed the schema. /// PropertyDescriptorDeleted A PropertyDescriptor was deleted, which changed the schema. /// PropertyDescriptorChanged A PropertyDescriptor was changed, which changed the schema. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public event ListChangedEventHandler ListChanged; void _raise_changed_event(ListChangedEventArgs e) { ListChangedEventHandler h = ListChanged; if (h != null) h.Invoke(this, e); } void _raise_reset_event() { _raise_changed_event(new ListChangedEventArgs(ListChangedType.Reset, -1)); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// /// IBindingList members /// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public object AddNew() { if (!f_allow_new) throw new InvalidOperationException("Cannot add a new item with this binding because it was constructed using an IList"); EndNew(0); return adding = _raise_add_new(); } public bool AllowEdit { get { return true; } } public bool AllowNew { get { return f_allow_new; } } public bool AllowRemove { get { return true; } } public bool SupportsChangeNotification { get { return true; } } public bool SupportsSorting { get { return true; } } public bool SupportsSearching { get { return true; } } public void AddIndex(PropertyDescriptor property) { throw new NotImplementedException(); } public void RemoveIndex(PropertyDescriptor property) { throw new NotImplementedException(); } public int Find(PropertyDescriptor property, object key) { throw new NotImplementedException(); } public void ApplySort(PropertyDescriptor property, ListSortDirection direction) { throw new NotImplementedException(); } public void RemoveSort() { throw new NotImplementedException(); } public bool IsSorted { get { throw new NotImplementedException(); } } public ListSortDirection SortDirection { get { throw new NotImplementedException(); } } public PropertyDescriptor SortProperty { get { throw new NotImplementedException(); } } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// /// AddingNew event : not from an interface /// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public event AddingNewEventHandler AddingNew; T _raise_add_new() { AddingNewEventHandler h = AddingNew; if (h != null) { AddingNewEventArgs e = new AddingNewEventArgs(); h.Invoke(this, e); return (T)e.NewObject; } T t = m_lt.New(); if (Object.Equals(t, default(T))) throw new InvalidOperationException(); return t; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// /// ICancelAddNew /// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public void CancelNew(int itemIndex) { adding = default(T); } public void EndNew(int itemIndex) { if (!Object.Equals(adding, default(T))) { T tmp = adding; adding = default(T); _insert(m_lt.Count, tmp); } } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// /// IList-to-IListTransactor adapter /// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// class ListAdapter : IListTransactor<T> { readonly IList<T> list; readonly Func<T> create_new; public ListAdapter(IList<T> list, Func<T> create_new) { this.list = list; this.create_new = create_new; } public void Insert(int index, T item) { list.Insert(index, item); } public int Count { get { return list.Count; } } public int IndexOf(T item) { return list.IndexOf(item); } public void RemoveAt(int index) { list.RemoveAt(index); } public void Clear() { list.Clear(); } public T Get(int index) { return list[index]; } public void Set(int index, T t) { list[index] = t; } public T New() { return create_new(); } }; }; }