using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using JD.Collections.Extensions;
using JD.Collections.Interfaces;
using JD.Kernel.StaticHelpers;
namespace JD.Collections
{
///
/// A Keyed collection that you can listen to through the CollectionChanged event. Differs from keyed collection in the fact the items are not stored in a list and in the collection.
///
public abstract class ObservableKeyedCollection : IObservableKeyedCollection
{
#region Variables
private readonly InnerCollection m_collection;
private int m_updateLevel;
#endregion
#region Constructor
///
/// Ctor
///
protected ObservableKeyedCollection()
: this(null)
{
}
///
/// Override default key Comparer
///
///
protected ObservableKeyedCollection(IEqualityComparer comparer)
{
m_collection = new InnerCollection(this, comparer);
}
#endregion
#region Methods
///
/// Get value from key.
///
///
///
/// Does not throw when the key is not contained.
public virtual TItem this[TKey key]
{
get
{
TItem value;
m_collection.TryGetValue(key, out value);
return value;
}
}
///
/// Removes the item with the given .
///
///
///
public bool Remove(TKey key)
{
TItem value;
if (m_collection.TryGetValue(key, out value))
{
return RemoveItem(value);
}
return false;
}
///
/// When implemented in a derived class, extracts the key from the specified element.
///
///
///
protected abstract TKey GetKeyForItem(TItem value);
///
/// Whether the keyed collection contains the key.
///
///
///
public virtual bool ContainsKey(TKey key)
{
return m_collection.ContainsKey(key);
}
///
/// Adds the item in the list.
///
///
public void Add(TItem item)
{
Throw.IfNull(item, "item");
InsertItem(item);
RegisterItem(item);
OnCollectionChanged(new CollectionChangedEventArgs(CollectionChangeAction.ItemAdded, item));
}
protected virtual void InsertItem(TItem item)
{
m_collection.Add(item);
}
public bool Remove(TItem item)
{
return RemoveItem(item);
}
protected virtual bool RemoveItem(TItem item)
{
bool removed = m_collection.Remove(item);
if (removed)
{
UnregisterItem(item);
OnCollectionChanged(new CollectionChangedEventArgs(CollectionChangeAction.ItemRemoved, item));
}
return removed;
}
///
/// Clears the list.
///
public virtual void Clear()
{
if (Count == 0)
{
return;
}
OnCollectionChanged(new CollectionChangedEventArgs(CollectionChangeAction.Clearing, default(TItem)));
m_collection.Clear();
OnCollectionChanged(new CollectionChangedEventArgs(CollectionChangeAction.Cleared, default(TItem)));
}
///
/// True if it contains the item.
///
///
///
public bool Contains(TItem item)
{
return !ReferenceEquals(item, null) && ContainsKey(GetKeyForItem(item));
}
public void CopyTo(TItem[] array, int arrayIndex)
{
Enumerable.AsEnumerable(this).CopyTo(array, arrayIndex);
}
public bool TryGetValue(TKey key, out TItem value)
{
return m_collection.TryGetValue(key, out value);
}
public int Count
{
get
{
return m_collection.Count;
}
}
public bool IsReadOnly
{
get { throw new NotImplementedException(); }
}
#endregion
#region ObservableCollectionTemplate
///
/// REMARK : This code is copy pasted from ObservableCollectionTemplate
///
/// Any modification should be applied to other observable collection.
///
///
/// Whether this set is updating.
///
public bool IsUpdating
{
get
{
return m_updateLevel > 0;
}
}
///
/// Suspends all events for this set.
///
public void BeginUpdate()
{
++m_updateLevel;
}
///
/// Resumes events for this set.
///
///
public bool EndUpdate()
{
if (--m_updateLevel == 0)
{
OnCollectionChanged(new CollectionChangedEventArgs(CollectionChangeAction.EndUpdated, default(TItem)));
return true;
}
return false;
}
///
/// Register an item in the list (for example, can bind to its events)
///
///
protected virtual void RegisterItem(TItem item)
{
IObservableItem observableItem = item as IObservableItem;
if (observableItem != null)
{
observableItem.Changed += internal_itemChanged;
}
}
///
/// Unregister an item from the list (for example, unbind from its events).
///
///
protected virtual void UnregisterItem(TItem item)
{
IObservableItem observableItem = item as IObservableItem;
if (observableItem != null)
{
observableItem.Changed -= internal_itemChanged;
}
}
#region Handlers
private void internal_itemChanged(object sender, EventArgs e)
{
if (sender is TItem)
{
OnCollectionChanged(new CollectionChangedEventArgs(CollectionChangeAction.ItemChanged, (TItem)sender));
}
else
{
System.Diagnostics.Debug.Assert(false, "Bad implementation of the IObservableItem interface. The object should be the sender of the event args");
OnCollectionChanged(new CollectionChangedEventArgs(CollectionChangeAction.ItemChanged, default(TItem)));
}
}
#endregion
#region Events
///
/// Triggered when the collection changes.
///
public event EventHandler> CollectionChanged;
///
/// Triggers the event.
///
/// Event Args.
protected virtual void OnCollectionChanged(CollectionChangedEventArgs e)
{
CollectionChanged?.Invoke(this, e);
}
#endregion
#endregion
#region Implementation of IEnumerable
public virtual IEnumerator GetEnumerator()
{
return m_collection.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
#region Nested
private class InnerCollection : KeyedCollection
{
private readonly ObservableKeyedCollection m_owner;
public InnerCollection(ObservableKeyedCollection owner, IEqualityComparer comparer)
: base(comparer)
{
m_owner = owner;
}
protected override TKey GetKeyForItem(TItem value)
{
return m_owner.GetKeyForItem(value);
}
}
#endregion
}
}