322 lines
No EOL
8.8 KiB
C#
322 lines
No EOL
8.8 KiB
C#
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
|
|
{
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
public abstract class ObservableKeyedCollection<TKey, TItem> : IObservableKeyedCollection<TKey, TItem>
|
|
{
|
|
#region Variables
|
|
|
|
private readonly InnerCollection m_collection;
|
|
private int m_updateLevel;
|
|
|
|
#endregion
|
|
|
|
#region Constructor
|
|
|
|
/// <summary>
|
|
/// Ctor
|
|
/// </summary>
|
|
protected ObservableKeyedCollection()
|
|
: this(null)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Override default key Comparer
|
|
/// </summary>
|
|
/// <param name="comparer"></param>
|
|
protected ObservableKeyedCollection(IEqualityComparer<TKey> comparer)
|
|
{
|
|
m_collection = new InnerCollection(this, comparer);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Methods
|
|
|
|
/// <summary>
|
|
/// Get value from key.
|
|
/// </summary>
|
|
/// <param name="key"></param>
|
|
/// <returns></returns>
|
|
/// <remarks>Does not throw when the key is not contained.</remarks>
|
|
public virtual TItem this[TKey key]
|
|
{
|
|
get
|
|
{
|
|
TItem value;
|
|
m_collection.TryGetValue(key, out value);
|
|
return value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes the item with the given <paramref name="key"/>.
|
|
/// </summary>
|
|
/// <param name="key"></param>
|
|
/// <returns></returns>
|
|
public bool Remove(TKey key)
|
|
{
|
|
TItem value;
|
|
if (m_collection.TryGetValue(key, out value))
|
|
{
|
|
return RemoveItem(value);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// When implemented in a derived class, extracts the key from the specified element.
|
|
/// </summary>
|
|
/// <param name="value"></param>
|
|
/// <returns></returns>
|
|
protected abstract TKey GetKeyForItem(TItem value);
|
|
|
|
/// <summary>
|
|
/// Whether the keyed collection contains the key.
|
|
/// </summary>
|
|
/// <param name="key"></param>
|
|
/// <returns></returns>
|
|
public virtual bool ContainsKey(TKey key)
|
|
{
|
|
return m_collection.ContainsKey(key);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds the item in the list.
|
|
/// </summary>
|
|
/// <param name="item"></param>
|
|
public void Add(TItem item)
|
|
{
|
|
Throw.IfNull(item, "item");
|
|
|
|
InsertItem(item);
|
|
RegisterItem(item);
|
|
OnCollectionChanged(new CollectionChangedEventArgs<TItem>(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<TItem>(CollectionChangeAction.ItemRemoved, item));
|
|
}
|
|
|
|
return removed;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clears the list.
|
|
/// </summary>
|
|
public virtual void Clear()
|
|
{
|
|
if (Count == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
OnCollectionChanged(new CollectionChangedEventArgs<TItem>(CollectionChangeAction.Clearing, default(TItem)));
|
|
m_collection.Clear();
|
|
OnCollectionChanged(new CollectionChangedEventArgs<TItem>(CollectionChangeAction.Cleared, default(TItem)));
|
|
}
|
|
|
|
/// <summary>
|
|
/// True if it contains the item.
|
|
/// </summary>
|
|
/// <param name="item"></param>
|
|
/// <returns></returns>
|
|
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
|
|
|
|
/// <summary>
|
|
/// REMARK : This code is copy pasted from ObservableCollectionTemplate
|
|
///
|
|
/// Any modification should be applied to other observable collection.
|
|
/// </summary>
|
|
|
|
/// <summary>
|
|
/// Whether this set is updating.
|
|
/// </summary>
|
|
public bool IsUpdating
|
|
{
|
|
get
|
|
{
|
|
return m_updateLevel > 0;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Suspends all events for this set.
|
|
/// </summary>
|
|
public void BeginUpdate()
|
|
{
|
|
++m_updateLevel;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Resumes events for this set.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public bool EndUpdate()
|
|
{
|
|
if (--m_updateLevel == 0)
|
|
{
|
|
OnCollectionChanged(new CollectionChangedEventArgs<TItem>(CollectionChangeAction.EndUpdated, default(TItem)));
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Register an item in the list (for example, can bind to its events)
|
|
/// </summary>
|
|
/// <param name="item"></param>
|
|
protected virtual void RegisterItem(TItem item)
|
|
{
|
|
IObservableItem observableItem = item as IObservableItem;
|
|
|
|
if (observableItem != null)
|
|
{
|
|
observableItem.Changed += internal_itemChanged;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Unregister an item from the list (for example, unbind from its events).
|
|
/// </summary>
|
|
/// <param name="item"></param>
|
|
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<TItem>(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<TItem>(CollectionChangeAction.ItemChanged, default(TItem)));
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Events
|
|
|
|
/// <summary>
|
|
/// Triggered when the collection changes.
|
|
/// </summary>
|
|
public event EventHandler<CollectionChangedEventArgs<TItem>> CollectionChanged;
|
|
|
|
/// <summary>
|
|
/// Triggers the <see cref="CollectionChanged"/> event.
|
|
/// </summary>
|
|
/// <param name="e">Event Args.</param>
|
|
protected virtual void OnCollectionChanged(CollectionChangedEventArgs<TItem> e)
|
|
{
|
|
CollectionChanged?.Invoke(this, e);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region Implementation of IEnumerable
|
|
|
|
public virtual IEnumerator<TItem> GetEnumerator()
|
|
{
|
|
return m_collection.GetEnumerator();
|
|
}
|
|
|
|
IEnumerator IEnumerable.GetEnumerator()
|
|
{
|
|
return GetEnumerator();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Nested
|
|
|
|
private class InnerCollection : KeyedCollection<TKey, TItem>
|
|
{
|
|
private readonly ObservableKeyedCollection<TKey, TItem> m_owner;
|
|
|
|
public InnerCollection(ObservableKeyedCollection<TKey, TItem> owner, IEqualityComparer<TKey> comparer)
|
|
: base(comparer)
|
|
{
|
|
m_owner = owner;
|
|
}
|
|
|
|
protected override TKey GetKeyForItem(TItem value)
|
|
{
|
|
return m_owner.GetKeyForItem(value);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
} |