C# Bindable Dictionary

I came across this BindableDictionary at StackOverflow and I thought I would improve on it and here is the result:

 
    public class BindableDictionary<TKey, TValue> : IDictionary<TKey, TValue>, IBindingList, IRaiseItemChangedEvents
    {
        public SortedList<TKey, TValue> _Source = null;
 
        private ListChangedEventHandler listChanged;
 
        private bool raiseItemChangedEvents = false;
        private bool raiseListChangedEvents = true;
 
        [NonSerialized()]
        private PropertyDescriptorCollection itemTypeProperties = null;
 
        [NonSerialized()]
        private PropertyChangedEventHandler propertyChangedEventHandler = null;
 
        [NonSerialized()]
        private int lastChangeIndex = -1;
 
        #region Properties
        #region IBindingList
        //Gets whether you can update items in the list. 
        bool IBindingList.AllowEdit
        {
            get
            {
                return true;
            }
        }
 
        //Gets whether you can add items to the list using AddNew. 
        bool IBindingList.AllowNew
        {
            get
            {
                return false;
            }
        }
 
        //Gets whether you can remove items from the list, using Remove or RemoveAt. 
        bool IBindingList.AllowRemove
        {
            get
            {
                return true;
            }
        }
 
 
        //Gets a value indicating whether the IList has a fixed size. (Inherited from IList.)
        bool System.Collections.IList.IsFixedSize
        {
            get { return false; }
        }
 
        //Gets a value indicating whether the IList is read-only. (Inherited from IList.)
        bool System.Collections.IList.IsReadOnly
        {
            get { return true; }
        }
 
        //Gets whether the items in the list are sorted. 
        bool IBindingList.IsSorted
        {
            get
            {
                return false;
            }
        }
 
        //Gets a value indicating whether access to the ICollection is synchronized (thread safe). (Inherited from ICollection.)
        bool ICollection.IsSynchronized
        {
            get { return false; }
        }
 
        //Gets or sets the element at the specified index. (Inherited from IList.)
        object System.Collections.IList.this[int index]
        {
            get { return _Source[_Source.Keys[index]]; }
            set { _Source[_Source.Keys[index]] = (TValue)value; }
        }
 
        //Gets the direction of the sort. 
        ListSortDirection IBindingList.SortDirection
        {
            get
            {
                return ListSortDirection.Ascending;
            }
        }
 
        //Gets the PropertyDescriptor that is being used for sorting. 
        PropertyDescriptor IBindingList.SortProperty
        {
            get { return null; }
        }
 
        //Gets whether a ListChanged event is raised when the list changes or an item in the list changes. 
        bool IBindingList.SupportsChangeNotification
        {
            get { return true; }
        }
 
        //Gets whether the list supports searching using the Find method. 
        bool IBindingList.SupportsSearching
        {
            get { return false; }
        }
 
        //Gets whether the list supports sorting. 
        bool IBindingList.SupportsSorting
        {
            get { return false; }
        }
 
        //Gets an object that can be used to synchronize access to the ICollection. (Inherited from ICollection.)
        object ICollection.SyncRoot
        {
            get { return null; }
        }
        #endregion
 
        #region IDictionary
        //Gets a value indicating whether the ICollection<(Of <(T>)>) is read-only. (Inherited from ICollection<(Of <(T>)>).)
        bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly
        {
            get { return ((ICollection<KeyValuePair<TKey, TValue>>)_Source).IsReadOnly; }
        }
 
        //Gets or sets the element with the specified key. 
        public TValue this[TKey key]
        {
            get
            {
                return _Source[key];
            }
            set
            {
                bool bAdded = false;
                if (_Source.ContainsKey(key))
                {
                    bAdded = true;
                }
                _Source[key] = value;
                if (bAdded)
                {
                    if (this.raiseItemChangedEvents)
                    {
                        HookPropertyChanged(value);
                        OnListChanged(new ListChangedEventArgs(ListChangedType.ItemAdded, _Source.IndexOfKey(key)));
                    }
                }
            }
        }
 
        //Gets an ICollection<(Of <(T>)>) containing the keys of the IDictionary<(Of <(TKey, TValue>)>). 
        public ICollection<TKey> Keys
        {
            get { return _Source.Keys; }
        }
 
        //Gets an ICollection<(Of <(T>)>) containing the values in the IDictionary<(Of <(TKey, TValue>)>). 
        public ICollection<TValue> Values
        {
            get { return _Source.Values; }
        }
 
        #endregion
 
        //Gets the number of elements contained in the ICollection. (Inherited from ICollection.)
        //Gets the number of elements contained in the ICollection<(Of <(T>)>). (Inherited from ICollection<(Of <(T>)>).)
        public int Count
        {
            get { return _Source.Count; }
        }
 
        #endregion
 
        #region Methods
        #region IBindingList
        //Add function not implemented use other Add function instead
        //Adds an item to the IList. (Inherited from IList.)
        int System.Collections.IList.Add(object value)
        {
            throw new NotImplementedException();
        }
 
        //Adds the PropertyDescriptor to the indexes used for searching.
        void IBindingList.AddIndex(PropertyDescriptor property)
        {
        }
 
        //Adds a new item to the list. 
        object IBindingList.AddNew()
        {
            return null;
        }
 
        //Sorts the list based on a PropertyDescriptor and a ListSortDirection. 
        void IBindingList.ApplySort(PropertyDescriptor property, ListSortDirection direction)
        {
        }
 
        //Determines whether the IList contains a specific value. (Inherited from IList.)
        bool System.Collections.IList.Contains(object value)
        {
            if (value is TKey)
            {
                return _Source.ContainsKey((TKey)value);
            }
            else if (value is TValue)
            {
                return _Source.ContainsValue((TValue)value);
            }
            return false;
        }
 
        //Copies the elements of the ICollection to an Array, starting at a particular Array index. (Inherited from ICollection.)
        void ICollection.CopyTo(Array array, int arrayIndex)
        {
            ((ICollection)_Source).CopyTo(array, arrayIndex);
        }
 
        //Returns the index of the row that has the given PropertyDescriptor. 
        int IBindingList.Find(PropertyDescriptor property, object key)
        {
            throw new NotImplementedException();
        }
 
        //Returns an enumerator that iterates through a collection. (Inherited from IEnumerable.)
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
 
        //Determines the index of a specific item in the IList. (Inherited from IList.)
        int System.Collections.IList.IndexOf(object value)
        {
 
            if (value is TKey)
            {
                return _Source.IndexOfKey((TKey)value);
            }
            else if (value is TValue)
            {
                return _Source.IndexOfValue((TValue)value);
            }
            return -1;
        }
 
        //Inserts an item to the IList at the specified index. (Inherited from IList.)
        void System.Collections.IList.Insert(int index, object value)
        {
            throw new NotImplementedException();
        }
 
        //Removes the first occurrence of a specific object from the IList. (Inherited from IList.)
        void System.Collections.IList.Remove(object value)
        {
            if (value is TKey)
            {
                Remove((TKey)value);
            }
        }
 
        //Removes the IList item at the specified index. (Inherited from IList.)
        void System.Collections.IList.RemoveAt(int index)
        {
            _Source.RemoveAt(index);
        }
 
        //Removes the PropertyDescriptor from the indexes used for searching. 
        void IBindingList.RemoveIndex(PropertyDescriptor property)
        {
        }
 
        //Removes any sort applied using ApplySort. 
        void IBindingList.RemoveSort()
        {
        }
        #endregion
 
 
        #region IDictionary
        //Adds an item to the ICollection<(Of <(T>)>). (Inherited from ICollection<(Of <(T>)>).)
        void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
        {
            ((ICollection<KeyValuePair<TKey, TValue>>)_Source).Add(item);
 
            if (this.raiseItemChangedEvents)
            {
                HookPropertyChanged(item.Value);
                OnListChanged(new ListChangedEventArgs(ListChangedType.ItemAdded, _Source.IndexOfKey(item.Key)));
            }
        }
 
        //Adds an element with the provided key and value to the IDictionary<(Of <(TKey, TValue>)>). 
        public void Add(TKey key, TValue value)
        {
            _Source.Add(key, value);
 
            if (this.raiseItemChangedEvents)
            {
                HookPropertyChanged(value);
                OnListChanged(new ListChangedEventArgs(ListChangedType.ItemAdded, _Source.IndexOfKey(key)));
            }
        }
 
        //Determines whether the ICollection<(Of <(T>)>) contains a specific value. (Inherited from ICollection<(Of <(T>)>).)
        bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
        {
            return ((ICollection<KeyValuePair<TKey, TValue>>)_Source).Contains(item);
        }
 
        //Determines whether the IDictionary<(Of <(TKey, TValue>)>) contains an element with the specified key. 
        public bool ContainsKey(TKey key)
        {
            return _Source.ContainsKey(key);
        }
 
        //Determines whether the IDictionary<(Of <(TKey, TValue>)>) contains an element with the specified key.
        void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
        {
            ((ICollection<KeyValuePair<TKey, TValue>>)_Source).CopyTo(array, arrayIndex);
        }
 
        //Returns an enumerator that iterates through the collection. (Inherited from IEnumerable<(Of <(T>)>).)
        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
        {
            return _Source.GetEnumerator();
        }
 
        //Removes the first occurrence of a specific object from the ICollection<(Of <(T>)>). (Inherited from ICollection<(Of <(T>)>).)
        bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
        {
            int index = _Source.IndexOfKey(item.Key);
            if (index != -1)
            {
                if (this.raiseItemChangedEvents)
                {
                    UnhookPropertyChanged(item.Value);
                }
                ((ICollection<KeyValuePair<TKey, TValue>>)_Source).Remove(item);
                OnListChanged(new ListChangedEventArgs(ListChangedType.ItemDeleted, index));
                return true;
            }
            return false;
        }
 
        //Removes the element with the specified key from the IDictionary<(Of <(TKey, TValue>)>). 
        public bool Remove(TKey key)
        {
            int index = _Source.IndexOfKey(key);
            if (index != -1)
            {
                if (this.raiseItemChangedEvents)
                {
                    UnhookPropertyChanged(_Source[key]);
                }
                _Source.Remove(key);
                OnListChanged(new ListChangedEventArgs(ListChangedType.ItemDeleted, index));
                return true;
            } return false;
        }
 
        //Gets the value associated with the specified key. 
        public bool TryGetValue(TKey key, out TValue value)
        {
            return _Source.TryGetValue(key, out value);
        }
        #endregion
 
        //Removes all items from the IList. (Inherited from IList.)
        //Removes all items from the ICollection<(Of <(T>)>). (Inherited from ICollection<(Of <(T>)>).)
        public void Clear()
        {
 
            if (this.raiseItemChangedEvents)
            {
                foreach (TValue item in _Source.Values)
                {
                    UnhookPropertyChanged(item);
                }
            }
 
            _Source.Clear();
            OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
        }
 
        private void Initialize()
        {
 
            // Check for INotifyPropertyChanged
            if (typeof(INotifyPropertyChanged).IsAssignableFrom(typeof(TValue)))
            {
                // Supports INotifyPropertyChanged
                this.raiseItemChangedEvents = true;
 
                // Loop thru the items already in the collection and hook their change notification.
                foreach (TValue item in _Source.Values)
                {
                    HookPropertyChanged(item);
                }
            }
        }
 
        //Send change notification
        protected virtual void OnListChanged(ListChangedEventArgs e)
        {
            var evt = listChanged; if (evt != null) evt(this, e);
        }
 
        public void ResetBindings()
        {
            OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
        }
 
        public bool RaiseListChangedEvents
        {
            get { return this.raiseListChangedEvents; }
 
            set
            {
                if (this.raiseListChangedEvents != value)
                {
                    this.raiseListChangedEvents = value;
                }
            }
        }
        #endregion
 
        #region Events
        #region IBindingList
        event ListChangedEventHandler IBindingList.ListChanged
        {
            add
            {
                listChanged += value;
            }
            remove { listChanged -= value; }
        }
        #endregion
        #endregion
 
        #region constructor
        public BindableDictionary()
        {
            _Source = new SortedList<TKey, TValue>();
            Initialize();
        }
 
        public BindableDictionary(IComparer<TKey> comparer)
        {
            _Source = new SortedList<TKey, TValue>(comparer);
            Initialize();
        }
        #endregion
 
 
        #region Property Change Support
 
        private void HookPropertyChanged(TValue item)
        {
            INotifyPropertyChanged inpc = (item as INotifyPropertyChanged);
 
            // Note: inpc may be null if item is null, so always check.
            if (null != inpc)
            {
                if (propertyChangedEventHandler == null)
                {
                    propertyChangedEventHandler = new PropertyChangedEventHandler(Child_PropertyChanged);
                }
                inpc.PropertyChanged += propertyChangedEventHandler;
            }
        }
 
        private void UnhookPropertyChanged(TValue item)
        {
            INotifyPropertyChanged inpc = (item as INotifyPropertyChanged);
 
            // Note: inpc may be null if item is null, so always check.
            if (null != inpc && null != propertyChangedEventHandler)
            {
                inpc.PropertyChanged -= propertyChangedEventHandler;
            }
        }
 
        void Child_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (this.RaiseListChangedEvents)
            {
                if (sender == null || e == null || string.IsNullOrEmpty(e.PropertyName))
                {
                    // Fire reset event (per INotifyPropertyChanged spec)
                    ResetBindings();
                }
                else
                {
                    TValue item;
 
                    try
                    {
                        item = (TValue)sender;
                    }
                    catch (InvalidCastException)
                    {
                        ResetBindings();
                        return;
                    }
 
                    // Find the position of the item. This should never be -1. If it is,
                    // somehow the item has been removed from our list without our knowledge.
                    int pos = lastChangeIndex;
 
                    if (pos < 0 || pos >= Count || !_Source.Values[pos].Equals(item))
                    {
                        pos = _Source.IndexOfValue(item);
                        lastChangeIndex = pos;
                    }
 
                    if (pos == -1)
                    {
                        UnhookPropertyChanged(item);
                        ResetBindings();
                    }
                    else
                    {
                        // Get the property descriptor
                        if (null == this.itemTypeProperties)
                        {
                            // Get Shape
                            itemTypeProperties = TypeDescriptor.GetProperties(typeof(TValue));
                        }
 
                        PropertyDescriptor pd = itemTypeProperties.Find(e.PropertyName, true);
 
                        // Create event args. If there was no matching property descriptor,
                        // we raise the list changed anyway.
                        ListChangedEventArgs args = new ListChangedEventArgs(ListChangedType.ItemChanged, pos, pd);
 
                        // Fire the ItemChanged event
                        OnListChanged(args);
                    }
                }
            }
        }
 
        #endregion
 
        #region IRaiseItemChangedEvents interface
 
        /// <include file='doc\BindingList.uex' path='docs/doc[@for="BindingList.RaisesItemChangedEvents"]/*' />
        /// <devdoc>
        /// Returns false to indicate that BindingList<T> does NOT raise ListChanged events
        /// of type ItemChanged as a result of property changes on individual list items
        /// unless those items support INotifyPropertyChanged
        /// </devdoc>
        bool IRaiseItemChangedEvents.RaisesItemChangedEvents
        {
            get { return this.raiseItemChangedEvents; }
        }
 
        #endregion
 
    }