Issue with ObservableStack<T> implementation and XAML Binding - c#

Where have I gone wrong with my implementation of a ObservableStack<T>? The XAML is failing to bind to it some how and so the information contained in the stack is not showing in the UI. If I only change the type of the property in the ViewModel from my ObservableStack<T> to ObservableCollection<T> then the UI elements appear. This makes me think it's the implementation.
I want to use this so my elements appear in the UI in Stack order and not collection order.
Here is my implementation:
public class ObservableStack<T> : INotifyPropertyChanged, INotifyCollectionChanged, ICollection , IEnumerable<T>, IEnumerable , IReadOnlyCollection<T>
{
ObservableCollection<T> _coll = new ObservableCollection<T>();
public ObservableStack()
{
_coll.CollectionChanged += _coll_CollectionChanged;
}
public ObservableStack(IEnumerable<T> items) : base()
{
foreach (var item in items)
{
Push(item);
}
}
private void _coll_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
OnCollectionChanged(e);
}
public void Push(T item)
{
_coll.Insert(0, item);
}
public void Pop()
{
_coll.RemoveAt(0);
}
public T Peek
{
get { return _coll[0]; }
}
public int Count
{
get
{
return ((ICollection)_coll).Count;
}
}
public bool IsSynchronized
{
get
{
return ((ICollection)_coll).IsSynchronized;
}
}
public object SyncRoot
{
get
{
return ((ICollection)_coll).SyncRoot;
}
}
public void CopyTo(Array array, int index)
{
((ICollection)_coll).CopyTo(array, index);
}
public IEnumerator GetEnumerator()
{
return ((ICollection)_coll).GetEnumerator();
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return ((IEnumerable<T>)_coll).GetEnumerator();
}
#region INotifyPropertyChanged
protected void OnPropertyChanged([CallerMemberName] string caller = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(caller));
}
public event PropertyChangedEventHandler PropertyChanged;
#endregion
#region INotifyCollectionChanged
public event NotifyCollectionChangedEventHandler CollectionChanged;
protected void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
CollectionChanged?.Invoke(this, e);
}
#endregion
}

You forgot to attach the internal CollectionChanged handler in the second constructor.
So call the other constructor (instead of the base class constructor) like this:
public ObservableStack(IEnumerable<T> items)
: this() // instead of base()
{
foreach (var item in items)
{
Push(item);
}
}

The solution I've found is that I needed to implement IList on my ObservableStack. After that everything worked as expected. Curiously, just implementing IList<T> didn't work. It needed the non generic interface. Thanks again to those who offered help.

Related

ISet<T> that notifies on add and remove?

I'd like an ISet<T> with two additional events ItemAdded and ItemRemoved.
One option I considered was deriving MyHashSet<T> from HashSet<T> but since Add and Remove are not virtual, it would require the use of new. Maybe this is a valid use of the keyword?
Another option I thought would be to implement ISet<T> and delegate everything to a private instance of HashSet<T>. This feels like a bulky solution.
Is there a pattern or framework class that gets me the same result but doesn't require less than elegant/ideal coding?
Based on the comments I received (thanks) here's what I've got:
public class NotifyingHashSet<T>
{
private HashSet<T> hashSet = new HashSet<T>();
public bool Add(T item)
{
bool added = hashSet.Add(item);
if(added && ItemAdded != null)
{
ItemAdded(this, new NotifyingHashSetEvent<T>(item));
}
return added;
}
public bool Remove(T item)
{
bool removed = hashSet.Remove(item);
if(removed && ItemRemoved != null)
{
ItemRemoved(this, new NotifyingHashSetEvent<T>(item));
}
return removed;
}
public event EventHandler<NotifyingHashSetEvent<T>> ItemAdded;
public event EventHandler<NotifyingHashSetEvent<T>> ItemRemoved;
}
public class NotifyingHashSetEvent<T> : EventArgs
{
public NotifyingHashSetEvent(T item)
{
Item = item;
}
public T Item { get; set; }
}
I would recommend inheriting rather than composing in this case.
This will ensure that you get all that HashSet offers like:
Other collection methods such as Contains and other Set operations such as IsSubsetOf
Collection initializers
You could assign it to base type, HashSet<int> foo = new NotifyingHashSet<int>()
My implementation looks like this:
public class NotifyingHashSet<T> : HashSet<T>
{
public new void Add(T item)
{
OnItemAdded(new NotifyHashSetChanged<T>(item));
base.Add(item);
}
public new void Remove(T item)
{
OnItemRemoved(new NotifyHashSetChanged<T>(item));
base.Remove(item);
}
public event EventHandler<NotifyHashSetChanged<T>> ItemAdded;
public event EventHandler<NotifyHashSetChanged<T>> ItemRemoved;
protected virtual void OnItemRemoved(NotifyHashSetChanged<T> e)
{
if (ItemRemoved != null) ItemRemoved(this, e);
}
protected virtual void OnItemAdded(NotifyHashSetChanged<T> e)
{
if (ItemAdded != null) ItemAdded(this, e);
}
}
public class NotifyHashSetChanged<T> : EventArgs
{
private readonly T _item;
public NotifyHashSetChanged(T item)
{
_item = item;
}
public T ChangedItem
{
get { return _item; }
}
}
Some tests to check stuff:
[TestClass]
public class NotifyingHashSetTests
{
[TestMethod]
public void ShouldAddToNotifyingHashSet()
{
var notifyingHashSet = new NotifyingHashSet<int>();
notifyingHashSet.ItemAdded += (sender, changed) => Assert.AreEqual(changed.ChangedItem, 1);
notifyingHashSet.Add(1);
}
[TestMethod]
public void ShouldRemoveFromNotifyingHashSet()
{
//can use collection initializer
var notifyingHashSet = new NotifyingHashSet<int> { 1 };
notifyingHashSet.ItemRemoved += (sender, changed) => Assert.AreEqual(changed.ChangedItem, 1);
notifyingHashSet.Remove(1);
}
[TestMethod]
public void ShouldContainValueInNotifyingHashSet()
{
//can use collection initializer
var notifyingHashSet = new NotifyingHashSet<int> { 1 };
Assert.IsTrue(notifyingHashSet.Contains(1));
}
[TestMethod]
public void ShouldAssignToHashSet()
{
HashSet<int> notifyingHashSet = new NotifyingHashSet<int> { 1 };
Assert.IsTrue(notifyingHashSet.IsSubsetOf(new List<int>{ 1,2 }));
}
}
Your own answer demonstrates how you can wrap a HashSet<T> and Srikanth's answer demonstrates how you can derive from HashSet<T>. However, when you derive from HashSet<T> you have to make sure the new class also correctly implements the Add and Remove methods of the ICollection<T> interface. So I have modified Srikanth's answer to properly create an ISet<T> implementation with notifications that derives from HashSet<T> by using explicit interface implementation of the relevant methods of ICollection<T>:
public class NotifyingHashSet<T> : HashSet<T>, ICollection<T> {
new public void Add(T item) {
((ICollection<T>) this).Add(item);
}
new public Boolean Remove(T item) {
return ((ICollection<T>) this).Remove(item);
}
void ICollection<T>.Add(T item) {
var added = base.Add(item);
if (added)
OnItemAdded(new NotifyHashSetEventArgs<T>(item));
}
Boolean ICollection<T>.Remove(T item) {
var removed = base.Remove(item);
if (removed)
OnItemRemoved(new NotifyHashSetEventArgs<T>(item));
return removed;
}
public event EventHandler<NotifyHashSetEventArgs<T>> ItemAdded;
public event EventHandler<NotifyHashSetEventArgs<T>> ItemRemoved;
protected virtual void OnItemRemoved(NotifyHashSetEventArgs<T> e) {
var handler = ItemRemoved;
if (handler != null)
ItemRemoved(this, e);
}
protected virtual void OnItemAdded(NotifyHashSetEventArgs<T> e) {
var handler = ItemAdded;
if (handler != null)
ItemAdded(this, e);
}
}
public class NotifyHashSetEventArgs<T> : EventArgs {
public NotifyHashSetEventArgs(T item) {
Item = item;
}
public T Item { get; private set; }
}
This class also behaves the same way as your class by only firing events when an element actually is added or removed from the set. E.g., adding the same element twice in succession will only fire one event.

C# - Call method when element is added to System.Collections.Generic.List [duplicate]

I have a list like this:
List<Controls> list = new List<Controls>
How to handle adding new position to this list?
When I do:
myObject.myList.Add(new Control());
I would like to do something like this in my object:
myList.AddingEvent += HandleAddingEvent
And then in my HandleAddingEvent delegate handling adding position to this list. How should I handle adding new position event? How can I make this event available?
I believe What you're looking for is already part of the API in the ObservableCollection(T) class. Example:
ObservableCollection<int> myList = new ObservableCollection<int>();
myList.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(
delegate(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
{
MessageBox.Show("Added value");
}
}
);
myList.Add(1);
You could inherit from List and add your own handler, something like
using System;
using System.Collections.Generic;
namespace test
{
class Program
{
class MyList<T> : List<T>
{
public event EventHandler OnAdd;
public new void Add(T item) // "new" to avoid compiler-warnings, because we're hiding a method from base-class
{
if (null != OnAdd)
{
OnAdd(this, null);
}
base.Add(item);
}
}
static void Main(string[] args)
{
MyList<int> l = new MyList<int>();
l.OnAdd += new EventHandler(l_OnAdd);
l.Add(1);
}
static void l_OnAdd(object sender, EventArgs e)
{
Console.WriteLine("Element added...");
}
}
}
Warning
Be aware that you have to re-implement all methods which add objects to your list. AddRange() will not fire this event, in this implementation.
We did not overload the method. We hid the original one. If you Add() an object while this class is boxed in List<T>, the event will not be fired!
MyList<int> l = new MyList<int>();
l.OnAdd += new EventHandler(l_OnAdd);
l.Add(1); // Will work
List<int> baseList = l;
baseList.Add(2); // Will NOT work!!!
What you need is a class that has events for any type of modification that occurs in the collection. The best class for this is BindingList<T>. It has events for every type of mutation which you can then use to modify your event list.
http://msdn.microsoft.com/en-us/library/ms132679.aspx
You can't do this with List<T>. However, you can do it with ObservableCollection<T>. See ObservableCollection<T> Class.
To be clear: If you only need to observe the standard-functionalities you should use ObservableCollection(T) or other existing classes. Never rebuild something you already got.
..But.. If you need special events and have to go deeper, you should not derive from List! If you derive from List you can not overloead Add() in order to see every add.
Example:
public class MyList<T> : List<T>
{
public void Add(T item) // Will show us compiler-warning, because we hide the base-mothod which still is accessible!
{
throw new Exception();
}
}
public static void Main(string[] args)
{
MyList<int> myList = new MyList<int>(); // Create a List which throws exception when calling "Add()"
List<int> list = myList; // implicit Cast to Base-class, but still the same object
list.Add(1); // Will NOT throw the Exception!
myList.Add(1); // Will throw the Exception!
}
It's not allowed to override Add(), because you could mees up the functionalities of the base class (Liskov substitution principle).
But as always we need to make it work. But if you want to build your own list, you should to it by implementing the an interface: IList<T>.
Example which implements a before- and after-add event:
public class MyList<T> : IList<T>
{
private List<T> _list = new List<T>();
public event EventHandler BeforeAdd;
public event EventHandler AfterAdd;
public void Add(T item)
{
// Here we can do what ever we want, buffering multiple events etc..
BeforeAdd?.Invoke(this, null);
_list.Add(item);
AfterAdd?.Invoke(this, null);
}
#region Forwarding to List<T>
public T this[int index] { get => _list[index]; set => _list[index] = value; }
public int Count => _list.Count;
public bool IsReadOnly => false;
public void Clear() => _list.Clear();
public bool Contains(T item) => _list.Contains(item);
public void CopyTo(T[] array, int arrayIndex) => _list.CopyTo(array, arrayIndex);
public IEnumerator<T> GetEnumerator() => _list.GetEnumerator();
public int IndexOf(T item) => _list.IndexOf(item);
public void Insert(int index, T item) => _list.Insert(index, item);
public bool Remove(T item) => _list.Remove(item);
public void RemoveAt(int index) => _list.RemoveAt(index);
IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator();
#endregion
}
Now we've got all methods we want and didn't have to implement much. The main change in our code is, that our variables will be IList<T> instead of List<T>, ObservableCollection<T> or what ever.
And now the big wow: All of those implement IList<T>:
IList<int> list1 = new ObservableCollection<int>();
IList<int> list2 = new List<int>();
IList<int> list3 = new int[10];
IList<int> list4 = new MyList<int>();
Which brings us to the next point: Use Interfaces instead of classes. Your code should never depend on implementation-details!
One simple solution is to introduce an Add method for the list in your project and handle the event there. It doesn't answer the need for an event handler but can be useful for some small projects.
AddToList(item) // or
AddTo(list,item)
////////////////////////
void AddTo(list,item)
{
list.Add(item);
// event handling
}
instead of
list.Add(item);
You cannot do this with standard collections out of the box - they just don't support change notifications. You could build your own class by inheriting or aggregating a existing collection type or you could use BindingList<T> that implements IBindingList and supports change notifications via the ListChanged event.
To piggy-back off Ahmad's use of Extension Methods, you can create your own class where the list is private with a public get method and a public add method.
public class MyList
{
private List<SomeClass> PrivateSomeClassList;
public List<SomeClass> SomeClassList
{
get
{
return PrivateSomeClassList;
}
}
public void Add(SomeClass obj)
{
// do whatever you want
PrivateSomeClassList.Add(obj);
}
}
However, this class only provides access to List<> methods that you manually expose...hence may not be useful in cases where you need a lot of that functionality.
No need for adding an event just add the method.
public class mylist:List<string>
{
public void Add(string p)
{
// Do cool stuff here
base.Add(p);
}
}
//List overrider class
public class ListWithEvents<T> : List<T>
{
public delegate void AfterChangeHandler();
public AfterChangeHandler OnChangeEvent;
public Boolean HasAddedItems = false;
public Boolean HasRemovedItems = false;
public bool HasChanges
{
get => HasAddedItems || HasRemovedItems;
set
{
HasAddedItems = value;
HasRemovedItems = value;
}
}
public new void Add(T item)
{
base.Add(item);
HasAddedItems = true;
if(OnChangeEvent != null) OnChangeEvent();
OnChange();
}
public new void AddRange(IEnumerable<T> collection)
{
base.AddRange(collection);
HasAddedItems = true;
if (OnChangeEvent != null) OnChangeEvent();
OnChange();
}
public new void Insert(int index,T item)
{
base.Insert(index, item);
HasAddedItems = true;
if (OnChangeEvent != null) OnChangeEvent();
OnChange();
}
public new void InsertRange(int index, IEnumerable<T> collection)
{
base.InsertRange(index, collection);
HasAddedItems = true;
if (OnChangeEvent != null) OnChangeEvent();
OnChange();
}
public new void Remove(T item)
{
base.Remove(item);
HasRemovedItems = true;
if (OnChangeEvent != null) OnChangeEvent();
OnChange();
}
public new void RemoveAt(int index)
{
HasRemovedItems = true;
if (OnChangeEvent != null) OnChangeEvent();
OnChange();
}
public new void RemoveRange(int index,int count)
{
HasRemovedItems = true;
if (OnChangeEvent != null) OnChangeEvent();
OnChange();
}
public new void Clear()
{
base.Clear();
HasRemovedItems = true;
if (OnChangeEvent != null) OnChangeEvent();
OnChange();
}
public virtual void OnChange()
{
}
}

List<T> firing Event on Change

I created a Class EventList inheriting List which fires an Event each time something is Added, Inserted or Removed:
public class EventList<T> : List<T>
{
public event ListChangedEventDelegate ListChanged;
public delegate void ListChangedEventDelegate();
public new void Add(T item)
{
base.Add(item);
if (ListChanged != null
&& ListChanged.GetInvocationList().Any())
{
ListChanged();
}
}
...
}
At the Moment I use it as a Property like this:
public EventList List
{
get { return m_List; }
set
{
m_List.ListChanged -= List_ListChanged;
m_List = value;
m_List.ListChanged += List_ListChanged;
List_ListChanged();
}
}
Now my Problem is, can I somehow handle if a new Object is referred to it or prevent that, so I do not have to do the event wiring stuff in the setter?
Of course, I can change the property to "private set" but I would like to be able to use the class as variable as well.
You seldom create a new instance of a collection class in a class. Instantiate it once and clear it instead of creating a new list. (and use the ObservableCollection since it already has the INotifyCollectionChanged interface inherited)
private readonly ObservableCollection<T> list;
public ctor() {
list = new ObservableCollection<T>();
list.CollectionChanged += listChanged;
}
public ObservableCollection<T> List { get { return list; } }
public void Clear() { list.Clear(); }
private void listChanged(object sender, NotifyCollectionChangedEventArgs args) {
// list changed
}
This way you only have to hook up events once, and can "reset it" by calling the clear method instead of checking for null or equality to the former list in the set accessor for the property.
With the changes in C#6 you can assign a get property from a constructor without the backing field (the backing field is implicit)
So the code above can be simplified to
public ctor() {
List = new ObservableCollection<T>();
List.CollectionChanged += OnListChanged;
}
public ObservableCollection<T> List { get; }
public void Clear()
{
List.Clear();
}
private void OnListChanged(object sender, NotifyCollectionChangedEventArgs args)
{
// react to list changed
}
ObservableCollection is a List with a CollectionChanged event
ObservableCollection.CollectionChanged Event
For how to wire up the event handler see answer from Patrick. +1
Not sure what you are looking for but I use this for a collection with one event that fires on add, remove, and change.
public class ObservableCollection<T>: INotifyPropertyChanged
{
private BindingList<T> ts = new BindingList<T>();
public event PropertyChangedEventHandler PropertyChanged;
// This method is called by the Set accessor of each property.
// The CallerMemberName attribute that is applied to the optional propertyName
// parameter causes the property name of the caller to be substituted as an argument.
private void NotifyPropertyChanged( String propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public BindingList<T> Ts
{
get { return ts; }
set
{
if (value != ts)
{
Ts = value;
if (Ts != null)
{
ts.ListChanged += delegate(object sender, ListChangedEventArgs args)
{
OnListChanged(this);
};
}
NotifyPropertyChanged("Ts");
}
}
}
private static void OnListChanged(ObservableCollection<T> vm)
{
// this will fire on add, remove, and change
// if want to prevent an insert this in not the right spot for that
// the OPs use of word prevent is not clear
// -1 don't be a hater
vm.NotifyPropertyChanged("Ts");
}
public ObservableCollection()
{
ts.ListChanged += delegate(object sender, ListChangedEventArgs args)
{
OnListChanged(this);
};
}
}
If you do not want to or can not convert to an Observable Collection, try this:
public class EventList<T> : IList<T> /* NOTE: Changed your List<T> to IList<T> */
{
private List<T> list; // initialize this in your constructor.
public event ListChangedEventDelegate ListChanged;
public delegate void ListChangedEventDelegate();
private void notify()
{
if (ListChanged != null
&& ListChanged.GetInvocationList().Any())
{
ListChanged();
}
}
public new void Add(T item)
{
list.Add(item);
notify();
}
public List<T> Items {
get { return list; }
set {
list = value;
notify();
}
}
...
}
Now, for your property, you should be able to reduce your code to this:
public EventList List
{
get { return m_List.Items; }
set
{
//m_List.ListChanged -= List_ListChanged;
m_List.Items = value;
//m_List.ListChanged += List_ListChanged;
//List_ListChanged();
}
}
Why? Setting anything in the EventList.Items will call your private notify() routine.
I have a Solution for when someone calls the Generic method from IList.add(object). So that you also get notified.
using System;
using System.Collections;
using System.Collections.Generic;
namespace YourNamespace
{
public class ObjectDoesNotMatchTargetBaseTypeException : Exception
{
public ObjectDoesNotMatchTargetBaseTypeException(Type targetType, object actualObject)
: base(string.Format("Expected base type ({0}) does not match actual objects type ({1}).",
targetType, actualObject.GetType()))
{
}
}
/// <summary>
/// Allows you to react, when items were added or removed to a generic List.
/// </summary>
public abstract class NoisyList<TItemType> : List<TItemType>, IList
{
#region Public Methods
/******************************************/
int IList.Add(object item)
{
CheckTargetType(item);
Add((TItemType)item);
return Count - 1;
}
void IList.Remove(object item)
{
CheckTargetType(item);
Remove((TItemType)item);
}
public new void Add(TItemType item)
{
base.Add(item);
OnItemAdded(item);
}
public new bool Remove(TItemType item)
{
var result = base.Remove(item);
OnItemRemoved(item);
return result;
}
#endregion
# region Private Methods
/******************************************/
private static void CheckTargetType(object item)
{
var targetType = typeof(TItemType);
if (item.GetType().IsSubclassOf(targetType))
throw new ObjectDoesNotMatchTargetBaseTypeException(targetType, item);
}
#endregion
#region Abstract Methods
/******************************************/
protected abstract void OnItemAdded(TItemType addedItem);
protected abstract void OnItemRemoved(TItemType removedItem);
#endregion
}
}
If an ObservableCollection is not the solution for you, you can try that:
A) Implement a custom EventArgs that will contain the new Count attribute when an event will be fired.
public class ChangeListCountEventArgs : EventArgs
{
public int NewCount
{
get;
set;
}
public ChangeListCountEventArgs(int newCount)
{
NewCount = newCount;
}
}
B) Implement a custom List that inherits from List and redefine the Count attribute and the constructors according to your needs:
public class CustomList<T> : List<T>
{
public event EventHandler<ChangeListCountEventArgs> ListCountChanged;
public new int Count
{
get
{
ListCountChanged?.Invoke(this, new ChangeListCountEventArgs(base.Count));
return base.Count;
}
}
public CustomList()
{ }
public CustomList(List<T> list) : base(list)
{ }
public CustomList(CustomList<T> list) : base(list)
{ }
}
C) Finally subscribe to your event:
var myList = new CustomList<YourObject>();
myList.ListCountChanged += (obj, e) =>
{
// get the count thanks to e.NewCount
};

How to have Lists with events upon change in List

I'm a newbie in events. This example shows that an event is invoked every time the ArrayList is changed. I would like to know how to do it using generics. To you implement IList or extend List? I tried to code it but I'm stuck.
using System;
using System.Collections.Generic;
namespace Events
{
public delegate void ChangedEventHandler(object sender, EventArgs e);
public class ListWithChangedEvent<T> : IList<T>
{
public event ChangedEventHandler Changed;
protected virtual void OnChanged(EventArgs e)
{
if (Changed != null)
{
Changed(this, e);
}
}
public void Add(T value)
{
base.Add(value);
OnChanged(EventArgs.Empty);
}
public void Clear()
{
base.Clear();
OnChanged(EventArgs.Empty);
}
public T this[int index]
{
set
{
base[index] = value;
OnChanged(EventArgs.Empty);
}
}
}
class EventListener
{
private ListWithChangedEvent<string> List;
public EventListener(ListWithChangedEvent<string> list)
{
List = list;
List.Changed += new ChangedEventHandler(ListChanged);
}
private void ListChanged(object sender, EventArgs e)
{
Console.WriteLine("This is called when the event fires.");
}
public void Detach()
{
List.Changed -= new ChangedEventHandler(ListChanged);
List = null;
}
}
class Program
{
public static void Main(string[] args)
{
ListWithChangedEvent<string> list = new ListWithChangedEvent<string>();
EventListener listener = new EventListener(list);
list.Add("item 1");
list.Clear();
listener.Detach();
}
}
}
You can use ObservableCollection and you can extend it !!
Namespace: System.Collections.ObjectModel
Assembly: WindowsBase (in WindowsBase.dll)
This collection fires events whenever the list is changed.(say any items are added, removed from list)
But note : The ObservableCollection doesnt fire events if the internal properties of objects it is holding changes. If you need that do let me know, I have extended the Observable collection to have that feature also.

How to handle add to list event?

I have a list like this:
List<Controls> list = new List<Controls>
How to handle adding new position to this list?
When I do:
myObject.myList.Add(new Control());
I would like to do something like this in my object:
myList.AddingEvent += HandleAddingEvent
And then in my HandleAddingEvent delegate handling adding position to this list. How should I handle adding new position event? How can I make this event available?
I believe What you're looking for is already part of the API in the ObservableCollection(T) class. Example:
ObservableCollection<int> myList = new ObservableCollection<int>();
myList.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(
delegate(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
{
MessageBox.Show("Added value");
}
}
);
myList.Add(1);
You could inherit from List and add your own handler, something like
using System;
using System.Collections.Generic;
namespace test
{
class Program
{
class MyList<T> : List<T>
{
public event EventHandler OnAdd;
public new void Add(T item) // "new" to avoid compiler-warnings, because we're hiding a method from base-class
{
if (null != OnAdd)
{
OnAdd(this, null);
}
base.Add(item);
}
}
static void Main(string[] args)
{
MyList<int> l = new MyList<int>();
l.OnAdd += new EventHandler(l_OnAdd);
l.Add(1);
}
static void l_OnAdd(object sender, EventArgs e)
{
Console.WriteLine("Element added...");
}
}
}
Warning
Be aware that you have to re-implement all methods which add objects to your list. AddRange() will not fire this event, in this implementation.
We did not overload the method. We hid the original one. If you Add() an object while this class is boxed in List<T>, the event will not be fired!
MyList<int> l = new MyList<int>();
l.OnAdd += new EventHandler(l_OnAdd);
l.Add(1); // Will work
List<int> baseList = l;
baseList.Add(2); // Will NOT work!!!
What you need is a class that has events for any type of modification that occurs in the collection. The best class for this is BindingList<T>. It has events for every type of mutation which you can then use to modify your event list.
http://msdn.microsoft.com/en-us/library/ms132679.aspx
You can't do this with List<T>. However, you can do it with ObservableCollection<T>. See ObservableCollection<T> Class.
To be clear: If you only need to observe the standard-functionalities you should use ObservableCollection(T) or other existing classes. Never rebuild something you already got.
..But.. If you need special events and have to go deeper, you should not derive from List! If you derive from List you can not overloead Add() in order to see every add.
Example:
public class MyList<T> : List<T>
{
public void Add(T item) // Will show us compiler-warning, because we hide the base-mothod which still is accessible!
{
throw new Exception();
}
}
public static void Main(string[] args)
{
MyList<int> myList = new MyList<int>(); // Create a List which throws exception when calling "Add()"
List<int> list = myList; // implicit Cast to Base-class, but still the same object
list.Add(1); // Will NOT throw the Exception!
myList.Add(1); // Will throw the Exception!
}
It's not allowed to override Add(), because you could mees up the functionalities of the base class (Liskov substitution principle).
But as always we need to make it work. But if you want to build your own list, you should to it by implementing the an interface: IList<T>.
Example which implements a before- and after-add event:
public class MyList<T> : IList<T>
{
private List<T> _list = new List<T>();
public event EventHandler BeforeAdd;
public event EventHandler AfterAdd;
public void Add(T item)
{
// Here we can do what ever we want, buffering multiple events etc..
BeforeAdd?.Invoke(this, null);
_list.Add(item);
AfterAdd?.Invoke(this, null);
}
#region Forwarding to List<T>
public T this[int index] { get => _list[index]; set => _list[index] = value; }
public int Count => _list.Count;
public bool IsReadOnly => false;
public void Clear() => _list.Clear();
public bool Contains(T item) => _list.Contains(item);
public void CopyTo(T[] array, int arrayIndex) => _list.CopyTo(array, arrayIndex);
public IEnumerator<T> GetEnumerator() => _list.GetEnumerator();
public int IndexOf(T item) => _list.IndexOf(item);
public void Insert(int index, T item) => _list.Insert(index, item);
public bool Remove(T item) => _list.Remove(item);
public void RemoveAt(int index) => _list.RemoveAt(index);
IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator();
#endregion
}
Now we've got all methods we want and didn't have to implement much. The main change in our code is, that our variables will be IList<T> instead of List<T>, ObservableCollection<T> or what ever.
And now the big wow: All of those implement IList<T>:
IList<int> list1 = new ObservableCollection<int>();
IList<int> list2 = new List<int>();
IList<int> list3 = new int[10];
IList<int> list4 = new MyList<int>();
Which brings us to the next point: Use Interfaces instead of classes. Your code should never depend on implementation-details!
One simple solution is to introduce an Add method for the list in your project and handle the event there. It doesn't answer the need for an event handler but can be useful for some small projects.
AddToList(item) // or
AddTo(list,item)
////////////////////////
void AddTo(list,item)
{
list.Add(item);
// event handling
}
instead of
list.Add(item);
You cannot do this with standard collections out of the box - they just don't support change notifications. You could build your own class by inheriting or aggregating a existing collection type or you could use BindingList<T> that implements IBindingList and supports change notifications via the ListChanged event.
To piggy-back off Ahmad's use of Extension Methods, you can create your own class where the list is private with a public get method and a public add method.
public class MyList
{
private List<SomeClass> PrivateSomeClassList;
public List<SomeClass> SomeClassList
{
get
{
return PrivateSomeClassList;
}
}
public void Add(SomeClass obj)
{
// do whatever you want
PrivateSomeClassList.Add(obj);
}
}
However, this class only provides access to List<> methods that you manually expose...hence may not be useful in cases where you need a lot of that functionality.
No need for adding an event just add the method.
public class mylist:List<string>
{
public void Add(string p)
{
// Do cool stuff here
base.Add(p);
}
}
//List overrider class
public class ListWithEvents<T> : List<T>
{
public delegate void AfterChangeHandler();
public AfterChangeHandler OnChangeEvent;
public Boolean HasAddedItems = false;
public Boolean HasRemovedItems = false;
public bool HasChanges
{
get => HasAddedItems || HasRemovedItems;
set
{
HasAddedItems = value;
HasRemovedItems = value;
}
}
public new void Add(T item)
{
base.Add(item);
HasAddedItems = true;
if(OnChangeEvent != null) OnChangeEvent();
OnChange();
}
public new void AddRange(IEnumerable<T> collection)
{
base.AddRange(collection);
HasAddedItems = true;
if (OnChangeEvent != null) OnChangeEvent();
OnChange();
}
public new void Insert(int index,T item)
{
base.Insert(index, item);
HasAddedItems = true;
if (OnChangeEvent != null) OnChangeEvent();
OnChange();
}
public new void InsertRange(int index, IEnumerable<T> collection)
{
base.InsertRange(index, collection);
HasAddedItems = true;
if (OnChangeEvent != null) OnChangeEvent();
OnChange();
}
public new void Remove(T item)
{
base.Remove(item);
HasRemovedItems = true;
if (OnChangeEvent != null) OnChangeEvent();
OnChange();
}
public new void RemoveAt(int index)
{
HasRemovedItems = true;
if (OnChangeEvent != null) OnChangeEvent();
OnChange();
}
public new void RemoveRange(int index,int count)
{
HasRemovedItems = true;
if (OnChangeEvent != null) OnChangeEvent();
OnChange();
}
public new void Clear()
{
base.Clear();
HasRemovedItems = true;
if (OnChangeEvent != null) OnChangeEvent();
OnChange();
}
public virtual void OnChange()
{
}
}

Categories