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(); }
};
};
}