ObservableDictionery をつくる

:::note warn
いまでも時々いいねやストックを頂きますが、こちらのライブラリを利用したほうが各確実です。

[Cysharp/ObservableCollections](https://github.com/Cysharp/ObservableCollections)
:::


XAML系テクノロジを使っていると時々欲しくなる ObservableDictionery<> について考えてみました。

まずは非ジェネリックの [DictionaryBase](http://msdn.microsoft.com/ja-jp/library/System.Collections.DictionaryBase.aspx) に相当するクラスをつくります。

[KeyedCollection<TKey, TItem>](http://msdn.microsoft.com/ja-jp/library/ms132438.aspx) というクラスが用意されているのでこれをベースにします。
これは Dictionary に近い挙動をする Collection の派生クラスです。（内部に Dictionary を持っています。）

これに IDictionary<> を実装させます。

```csharp:CollectionBasedDictionary.cs
public class CollectionBasedDictionary<TKey, TValue>
    : KeyedCollection<TKey, KeyValuePair<TKey, TValue>>, IDictionary<TKey, TValue> // 必要なら IDictionary も
{
    /*
     * Dictionary を参考に コンストラクタを記述
     */

    protected override TKey GetKeyForItem(KeyValuePair<TKey, TValue> item)
    {
        return item.Key;
    }

    /*
     * IDictionery<TKey, TValue> のメンバー を記述
     * IDictionery<TKey, TValue>.Values の実装がちょっとめんどくさいです…
     */
}
```

あとは、CollectionBasedDictionary に 対して Collection -> ObsservableCollection と同じ拡張をすればOKです。

```csharp:ObservableDictionery.cs
public class ObservableDictionary<TKey, TValue>
    : CollectionBasedDictionary<TKey, TValue>, INotifyCollectionChanged, INotifyPropertyChanged
{
    /*
     * Dictionary を参考に コンストラクタを記述
     */

    public virtual event NotifyCollectionChangedEventHandler CollectionChanged;
    protected virtual event PropertyChangedEventHandler PropertyChanged;

    event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
    {
        add
        {
            PropertyChanged += value;
        }
        remove
        {
            PropertyChanged -= value;
        }
    }

    protected const string IndexerName = "Item[]";
    protected const string CountName = "Count";

    protected override void InsertItem(int index, KeyValuePair<TKey, TValue> item)
    {
        base.InsertItem(index, item);
        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, newItem, index));
        OnPropertyChanged(IndexerName);
        OnPropertyChanged(CountName);
    }

    protected override void RemoveItem(int index)
    {
        var item = this[index];
        base.RemoveItem(index);
        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, oldItem, index));
        OnPropertyChanged(IndexerName);
        OnPropertyChanged(CountName);
    }

    protected override void SetItem(int index, KeyValuePair<TKey, TValue> item)
    {
        var oldItem = this[index];
        base.SetItem(index, item);
        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, newItem, oldItem, index));
        OnPropertyChanged(IndexerName);
    }

    protected override void ClearItems()
    {
        base.ClearItems();
        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        OnPropertyChanged(IndexerName);
        OnPropertyChanged(CountName);
    }

    protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        if (CollectionChanged != null)
        {
            CollectionChanged(this, e);
        }
    }

    protected virtual void OnPropertyChanged(string name)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(name));
        }
    }
}
```

この方法だと  ObservableDictionery が IList<> を実装しているので、 Livet の DispatcherCollection 等にも使用できます。