OK; here is a pretty-bare-bones implementation that supports change
notification of items via INotifyPropertyChanged...
You might also consider adding support for ICancelAddNew, but this interface
is pretty backwards in my opinion - I suspect it was designed to fit how
DataTable worked at the time, rather than "how should this be done?" - and
it is *not* as trivial as removing the row...
Note that if you provide a proper ApplySort implemention then users will be
able to sort via the column headers; I've posted examples of this
previously.
I haven't added many comments due to time; but please ask any questions you
like. The main thing to note is how anything adding/removing/replacing items
in the list uses WatchForChange to subscribe/unsubscribe to the
PropertyChanged event.
Anyways... [long code warning]
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
class Bar : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void UpdateField<T>(ref T field, T value, string propertyName)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return;
field = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new
PropertyChangedEventArgs(propertyName));
}
}
private string name;
private DateTime dateOfBirth;
public string Name
{
get { return name; }
set { UpdateField(ref name, value, "Name"); }
}
[DisplayName("Date of Birth")]
public DateTime DateOfBirth
{
get { return dateOfBirth; }
set { UpdateField(ref dateOfBirth, value, "DateOfBirth"); }
}}
class Program
{
[STAThread]
static void Main()
{
FooList<Bar> data = new FooList<Bar>();
Random rand = new Random();
Application.EnableVisualStyles();
using(Form form = new Form())
using(DataGridView view = new DataGridView())
using(Button btn1 = new Button())
using(Button btn2 = new Button())
using (Button btn3 = new Button())
using (Button btn4 = new Button())
{
view.Dock = DockStyle.Fill;
btn1.Dock = btn2.Dock = btn3.Dock = btn4.Dock =
DockStyle.Bottom;
view.DataSource = data;
form.Controls.AddRange(new Control[] { view, btn1,btn2, btn3,
btn4 });
btn1.Text = "Clear";
btn1.Click += delegate { data.Clear(); };
btn2.Text = "Insert";
btn2.Click += delegate {
Bar b = new Bar();
b.Name = "New bar";
int index = data.Count == 0 ? 0 : rand.Next(0, data.Count);
data.Insert(index, b);
};
btn3.Text = "Remove";
btn3.Click += delegate
{
if (data.Count > 0)
{
data.RemoveAt(rand.Next(0, data.Count));
}
};
btn4.Text = "Update";
btn4.Click += delegate
{
if (data.Count > 0)
{
data[rand.Next(0, data.Count)].Name += "*";
}
};
Application.Run(form);
}
}
}
class FooList<T> : IList<T>, IBindingList, IRaiseItemChangedEvents
{
object ICollection.SyncRoot { get { return
((ICollection)InnerList).SyncRoot; } }
bool ICollection.IsSynchronized { get { return
((ICollection)InnerList).IsSynchronized; } }
public int IndexOf(T item) { return InnerList.IndexOf(item); }
int IList.IndexOf(object obj) { return IndexOf((T)obj); }
bool IList.Contains(object obj) { return Contains((T)obj); }
void IList.Remove(object obj) { Remove((T)obj); }
public bool Contains(T item) {return InnerList.Contains(item);}
public void CopyTo(T[] array, int arrayIndex) { InnerList.CopyTo(array,
arrayIndex); }
void ICollection.CopyTo(Array array, int arrayIndex) {
((ICollection)InnerList).CopyTo(array, arrayIndex); }
public int Count { get { return InnerList.Count; } }
void IList.Insert(int index, object obj) { Insert(index, (T)obj); }
bool ICollection<T>.IsReadOnly { get { return false; } }
bool IList.IsReadOnly { get { return false; } }
bool IBindingList.AllowNew { get { return true; } }
bool IList.IsFixedSize { get { return false; } }
bool IBindingList.AllowEdit { get { return true; } }
bool IBindingList.AllowRemove { get { return true; } }
public IEnumerator<T> GetEnumerator() { return
InnerList.GetEnumerator(); }
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
void IBindingList.AddIndex(PropertyDescriptor prop) { }
void IBindingList.RemoveIndex(PropertyDescriptor prop) { }
bool IBindingList.SupportsSorting { get { return false; } }
bool IBindingList.IsSorted { get { return false; } }
void IBindingList.ApplySort(PropertyDescriptor prop, ListSortDirection
dir) { throw new NotSupportedException(); }
void IBindingList.RemoveSort() { }
PropertyDescriptor IBindingList.SortProperty { get { return null; } }
ListSortDirection IBindingList.SortDirection { get { return
ListSortDirection.Ascending; } }
bool IBindingList.SupportsSearching { get { return false; } }
int IBindingList.Find(PropertyDescriptor prop, object obj) { thrownew
NotSupportedException(); }
bool IBindingList.SupportsChangeNotification { get { return true; } }
object IList.this[int index]
{
get { return this[index]; }
set { this[index] = (T)value; }
}
private readonly List<T> InnerList;
public FooList()
{
InnerList = new List<T>();
}
private static readonly bool raisesItemChangedEvents;
static FooList()
{
foreach (Type type in typeof(T).GetInterfaces())
{
if (type == typeof(INotifyPropertyChanged))
{
raisesItemChangedEvents = true;
break;
}
}
}
bool IRaiseItemChangedEvents.RaisesItemChangedEvents {
get {return raisesItemChangedEvents;}
}
// useful for major changes; sort etc
//(suspends notificaction then does reset)
private int editCount;
public void BeginEdit() { editCount++; }
public void EndEdit()
{
if (editCount <= 0) throw new InvalidOperationException();
editCount--;
if (editCount == 0) OnListChanged(ListChangedType.Reset, -1);
}
private bool NotificationEnabled
{
get { return editCount == 0; }
}
public event ListChangedEventHandler ListChanged;
private void OnListChanged(ListChangedType changeType, int index)
{
if (NotificationEnabled && ListChanged != null)
{
ListChanged(this, new ListChangedEventArgs(changeType, index,
index));
}
}
public T AddNew() {
// note: gets more complicated if you support ICancelAddNew
T item = Activator.CreateInstance<T>();
Add(item);
return item;
}
object IBindingList.AddNew()
{
return AddNew();
}
void WatchForChange(T item, bool enable)
{
INotifyPropertyChanged npc = item as INotifyPropertyChanged;
if (npc != null)
{
if (enable)
{
npc.PropertyChanged += ItemPropertyChanged;
}
else
{
npc.PropertyChanged -= ItemPropertyChanged;
}
}
}
void ItemPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if(sender is T) {
int index = IndexOf((T)sender);
if(index>=0) {
OnListChanged(ListChangedType.ItemChanged,index);
}
}
}
public T this[int index]
{
get { return InnerList[index]; }
set
{
// if same reference, then ignore
T oldItem = this[index];
if (ReferenceEquals(oldItem, value)) return;
InnerList[index] = value;
WatchForChange(oldItem, false);
WatchForChange(value, true);
OnListChanged(ListChangedType.ItemChanged, index);
}
}
int IList.Add(object obj) { return InnerAdd((T)obj); }
public void Add(T item) { InnerAdd(item); }
private int InnerAdd(T item)
{
int index = InnerList.Count;
InnerList.Add(item);
WatchForChange(item, true);
OnListChanged(ListChangedType.ItemAdded, index);
return index;
}
public void Insert(int index, T item)
{
InnerList.Insert(index, item);
WatchForChange(item, true);
OnListChanged(ListChangedType.ItemAdded, index);
}
public bool Remove(T item)
{
int index = IndexOf(item);
if (index < 0) return false;
RemoveAt(index);
return true;
}
public void RemoveAt(int index)
{
T item = this[index];
InnerList.RemoveAt(index);
WatchForChange(item, false);
OnListChanged(ListChangedType.ItemDeleted, index);
}
public void Clear()
{
foreach (T item in InnerList)
{
WatchForChange(item, false);
}
InnerList.Clear();
OnListChanged(ListChangedType.Reset, -1);
}
}- Hide quoted text -
- Show quoted text -