//The MIT License(MIT)
//Copyright(c) 2016 Alberto Rodriguez & LiveCharts Contributors
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
namespace LiveCharts.Helpers
{
///
///
///
///
/// The old items.
/// The new items.
public delegate void NoisyCollectionCollectionChanged(
IEnumerable oldItems, IEnumerable newItems);
///
///
///
///
public interface INoisyCollection : IList, INotifyPropertyChanged, INotifyCollectionChanged
{
///
/// Occurs when [noisy collection changed].
///
event NoisyCollectionCollectionChanged NoisyCollectionChanged;
///
/// Adds the range.
///
/// The items.
void AddRange(IEnumerable items);
///
/// Inserts the range.
///
/// The index.
/// The collection.
void InsertRange(int index, IEnumerable collection);
}
///
/// A collection that notifies every time a value is added or removed
///
///
public class NoisyCollection : INoisyCollection, IList
{
#region Private Fields
private readonly object _sync = new object();
private readonly List _source;
private const string CountString = "Count";
private const string IndexerString = "Item[]";
#endregion
#region Constructors
///
/// Initializes a new instance of NoisyCollection class
///
public NoisyCollection()
{
_source = new List();
}
///
/// Initializes a new instance of NoisyCollection class with a given collection
///
/// given collection
public NoisyCollection(IEnumerable collection)
{
_source = new List(collection);
}
///
/// Initializes a new instance of NoisiCollection class with a given capacity
///
/// given capacity
public NoisyCollection(int capacity)
{
_source = new List(capacity);
}
#endregion
#region Events
///
/// Occurs when [collection reset].
///
public event Action CollectionReset;
///
/// Occurs when [noisy collection changed].
///
event NoisyCollectionCollectionChanged INoisyCollection.NoisyCollectionChanged
{
add { NoisyCollectionChanged += value as NoisyCollectionCollectionChanged; }
remove { NoisyCollectionChanged -= value as NoisyCollectionCollectionChanged; }
}
///
/// Occurs when [noisy collection changed].
///
public event NoisyCollectionCollectionChanged NoisyCollectionChanged;
///
/// Occurs when the collection changes.
///
public virtual event NotifyCollectionChangedEventHandler CollectionChanged;
///
/// Occurs when a property value changes.
///
protected virtual event PropertyChangedEventHandler PropertyChanged;
///
/// Occurs when a property value changes.
///
event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
{
add
{
PropertyChanged += value;
}
remove
{
PropertyChanged -= value;
}
}
#endregion
#region Properties
///
/// Gets or sets an item from/in a specific index
///
/// index to get/set
///
public T this[int index]
{
get
{
lock (_sync)
{
return _source[index];
}
}
set
{
var original = this[index];
lock (_sync)
{
_source[index] = value;
}
ReplaceItem(original, value, index);
}
}
///
/// Gets or sets an item from/in a specific index
///
/// index to get/set
///
object IList.this[int index]
{
get
{
lock (_sync)
{
return _source[index];
}
}
set
{
var original = this[index];
lock (_sync)
{
_source[index] = (T)value;
}
ReplaceItem(original, value, index);
}
}
///
/// Enumerates the collection
///
/// collection enumeration
public IEnumerator GetEnumerator()
{
lock (_sync)
{
return new List(_source).GetEnumerator();
}
}
///
/// Enumerates the collection
///
/// collection enumeration
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
///
/// Gets the number of items in the array
///
/// items count
public int Count
{
get { return _source.Count; }
}
///
/// Gets whether the collection is read only
///
/// result
bool ICollection.IsReadOnly
{
get { return ((ICollection)_source).IsReadOnly; }
}
///
/// Gets the number of items in the array
///
/// result
bool IList.IsReadOnly
{
get { return ((IList)_source).IsReadOnly; }
}
///
/// Gets whether the collection is synchronized
///
/// result
public bool IsSynchronized
{
get { return ((ICollection)_source).IsSynchronized; }
}
///
/// Gets the collections's sync root
///
public object SyncRoot
{
get { return ((ICollection)_source).SyncRoot; }
}
///
/// Gets whether the collection is fixed
///
public bool IsFixedSize { get { return ((IList)_source).IsFixedSize; } }
#endregion
#region Public Methods
///
/// Adds an object to the collection, and notifies the change
///
/// item to add
/// number of items in the collection
int IList.Add(object value)
{
var v = (T)value;
Add(v);
lock (_sync)
{
return _source.IndexOf(v);
}
}
///
/// Add an item to the collection, and notifies the change
///
/// number of items in the collection
public void Add(T item)
{
lock (_sync)
{
_source.Add(item);
}
OnNoisyCollectionChanged(null, new[] { item });
OnPropertyChanged(CountString);
OnPropertyChanged(IndexerString);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(
NotifyCollectionChangedAction.Add, item, _source.Count - 1));
}
///
/// Adds many items to the collection, and notifies the change
///
/// collection to add
public void AddRange(IEnumerable items)
{
AddRange(items.Cast());
}
///
/// Adds many items to the collection, and notifies the change
///
/// collection to add
public void AddRange(IEnumerable items)
{
var newItems = items as T[] ?? items.ToArray();
lock (_sync)
{
_source.AddRange(newItems);
}
OnNoisyCollectionChanged(null, newItems);
OnPropertyChanged(CountString);
OnPropertyChanged(IndexerString);
//This scenario is not supported normally in ObservableCollections
//in this case we'll send a reset action.
OnCollectionChanged(new NotifyCollectionChangedEventArgs(
NotifyCollectionChangedAction.Reset));
}
///
/// Insert an item in a specific index, then notifies the change
///
/// index to insert at
/// item to insert
public void Insert(int index, T item)
{
lock (_sync)
{
_source.Insert(index, item);
}
OnNoisyCollectionChanged(null, new[] { item });
OnPropertyChanged(CountString);
OnPropertyChanged(IndexerString);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(
NotifyCollectionChangedAction.Add, item, index));
}
///
/// Insert an item in a specific index, then notifies the change
///
/// index to insert at
/// item to insert
public void Insert(int index, object value)
{
Insert(index, (T)value);
}
///
/// Insert a range of values, starting in a specific index, then notifies the change
///
/// index to start at
/// collection to insert
public void InsertRange(int index, IEnumerable collection)
{
InsertRange(index, collection.Cast());
}
///
/// Insert a range of values, starting in a specific index, then notifies the change
///
/// index to start at
/// collection to insert
public void InsertRange(int index, IEnumerable collection)
{
var newItems = collection as T[] ?? collection.ToArray();
lock (_sync)
{
_source.InsertRange(index, newItems);
}
OnNoisyCollectionChanged(null, newItems);
OnPropertyChanged(CountString);
OnPropertyChanged(IndexerString);
//This scenario is not supported normally in ObservableCollections
//in this case we'll send a reset action.
OnCollectionChanged(new NotifyCollectionChangedEventArgs(
NotifyCollectionChangedAction.Reset));
}
///
/// Removes an item from a collection, then notifies the change
///
/// item to remove
public void Remove(object value)
{
Remove((T)value);
}
///
/// Remove an item from a collection, then notifies the change
///
/// item to remove
/// number of items in the collection
public bool Remove(T item)
{
int index;
lock (_sync)
{
index = _source.IndexOf(item);
}
if (index < 0) return false;
RemoveAt(index);
return true;
}
///
/// Removes an item at a specific index, then notifies the change
///
/// index to remove at
void IList.RemoveAt(int index)
{
RemoveAt(index);
}
///
/// Removes an item at a specific index, then notifies the change
///
/// index to remove at
void IList.RemoveAt(int index)
{
RemoveAt(index);
}
///
/// Removes an item at a specific index, then notifies the change
///
/// index to remove at
public void RemoveAt(int index)
{
T item;
lock (_sync)
{
item = _source[index];
_source.RemoveAt(index);
}
OnNoisyCollectionChanged(new[] { item }, null);
OnPropertyChanged(CountString);
OnPropertyChanged(IndexerString);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(
NotifyCollectionChangedAction.Remove, item, index));
}
///
/// Removes all the items from the collection, then notifies the change
///
void IList.Clear()
{
Clear();
}
///
/// Removes all the items from the collection, then notifies the change
///
void ICollection.Clear()
{
Clear();
}
///
/// Removes all the items from the collection, then notifies the change
///
public void Clear()
{
T[] backup;
lock (_sync)
{
backup = _source.ToArray();
_source.Clear();
}
OnNoisyCollectionChanged(backup, null);
OnNoisyCollectionReset();
OnPropertyChanged(CountString);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(
NotifyCollectionChangedAction.Reset));
}
///
/// Evaluates whether an item is in this collection
///
/// object to look for
/// evaluation
public bool Contains(object value)
{
return Contains((T)value);
}
///
/// Evaluates whether an item is in this collection
///
/// item to look for
/// evaluation
public bool Contains(T item)
{
lock (_sync)
{
return _source.Contains(item);
}
}
///
/// Copies the collection to another array
///
/// backup array
/// array index
public void CopyTo(Array array, int index)
{
CopyTo(array.Cast().ToArray(), index);
}
///
/// Copies the collection to another array
///
/// backup array
/// array index
public void CopyTo(T[] array, int index)
{
lock (_sync)
{
_source.CopyTo(array, index);
}
}
///
/// Returns the index of an item in the collection
///
/// item to look for
///
public int IndexOf(object value)
{
return IndexOf((T)value);
}
///
/// Returns the index of an item in the collection
///
/// item to look for
///
public int IndexOf(T item)
{
lock (_sync)
{
return _source.IndexOf(item);
}
}
#endregion
#region Protected Methods
///
/// Raises the event.
///
/// The instance containing the event data.
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
{
PropertyChanged.Invoke(this, e);
}
}
///
/// Raises the event.
///
/// The instance containing the event data.
protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (CollectionChanged != null)
{
CollectionChanged.Invoke(this, e);
}
}
#endregion
#region Private Methods
private void OnNoisyCollectionChanged(IEnumerable olditems, IEnumerable newItems)
{
if (NoisyCollectionChanged != null)
NoisyCollectionChanged.Invoke(olditems, newItems);
}
private void OnNoisyCollectionReset()
{
if (CollectionReset != null)
CollectionReset.Invoke();
}
private void OnPropertyChanged(string propertyName)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
private void ReplaceItem(object original, object item, int index)
{
OnPropertyChanged(IndexerString);
OnNoisyCollectionChanged(new List{(T) original}, new List{(T) item});
OnCollectionChanged(new NotifyCollectionChangedEventArgs(
NotifyCollectionChangedAction.Replace, original, item, index));
}
#endregion
}
}