JD2022-TU1/main/tools/framework/JD.Collections/ObservableKeyedCollection.cs

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
}
}