File: src\Framework\MS\Internal\Annotations\ObservableDictionary.cs
Project: wpf\PresentationFramework.csproj (PresentationFramework)
//-----------------------------------------------------------------------------
//
// <copyright file="ObservableDictionary.cs" company="Microsoft">
//    Copyright (C) Microsoft Corporation.  All rights reserved.
// </copyright>
//
// Description:
//     ContentLocatorPart represents a set of name/value pairs that identify a
//     piece of data within a certain context.  The names and values are
//     strings.
//
//     Spec: http://team/sites/ag/Specifications/Simplifying%20Store%20Cache%20Model.doc
//
// History:
//  05/06/2004: ssimova:  Created
//  06/30/2004: rruiz:    Added change notifications to parent, clean-up
//-----------------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
 
 
namespace MS.Internal.Annotations
{
    /// <summary>
    ///     ContentLocatorPart represents a set of name/value pairs that identify a
    ///     piece of data within a certain context.  The names and values are
    ///     all strings.
    /// </summary>
    internal class ObservableDictionary : IDictionary<string, string>, INotifyPropertyChanged
    {
        //------------------------------------------------------
        //
        //  Constructors
        //
        //------------------------------------------------------
 
        #region Constructors
 
        /// <summary>
        ///     Creates a ContentLocatorPart with the specified type name and namespace.
        /// </summary>
        public ObservableDictionary()
        {
            _nameValues = new Dictionary<string,string>();
        }
 
        #endregion Constructors
 
        //------------------------------------------------------
        //
        //  Public Methods
        //
        //------------------------------------------------------
 
        #region Public Methods
 
        /// <summary>
        ///     Adds a key/value pair to the ContentLocatorPart.  If a value for the key already
        ///     exists, the old value is overwritten by the new value.
        /// </summary>
        /// <param name="key">key</param>
        /// <param name="val">value</param>
        /// <exception cref="ArgumentNullException">key or val is null</exception>
        /// <exception cref="ArgumentException">a value for key is already present in the locator part</exception>
        public void Add(string key, string val)
        {
            if (key == null || val == null)
            {
                throw new ArgumentNullException(key == null ? "key" : "val");
            }
            _nameValues.Add(key, val);
            FireDictionaryChanged();
        }
 
        /// <summary>
        ///     Removes all name/value pairs from the ContentLocatorPart.
        /// </summary>
        public void Clear()
        {
            int count = _nameValues.Count;
 
            if (count > 0)
            {
                _nameValues.Clear();
 
                // Only fire changed event if the dictionary actually changed
                FireDictionaryChanged();
            }
        }
 
        /// <summary>
        ///     Returns whether or not a value of the key exists in this ContentLocatorPart.
        /// </summary>
        /// <param name="key">the key to check for</param>
        /// <returns>true - yes, false - no</returns>
        public bool ContainsKey(string key)
        {
            return _nameValues.ContainsKey(key);
        }
 
        /// <summary>
        ///     Removes the key and its value from the ContentLocatorPart.
        /// </summary>
        /// <param name="key">key to be removed</param>
        /// <returns>true - the key was found in the ContentLocatorPart, false o- it wasn't</returns>
        public bool Remove(string key)
        {
            bool exists = _nameValues.Remove(key);
 
            // Only fire changed event if the key was actually removed
            if (exists)
            {
                FireDictionaryChanged();
            }
 
            return exists;
        }
 
        /// <summary>
        ///     Returns an enumerator for the key/value pairs in this ContentLocatorPart.
        /// </summary>
        /// <returns>an enumerator for the key/value pairs; never returns null</returns>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return _nameValues.GetEnumerator();
        }
 
        /// <summary>
        ///     Returns an enumerator forthe key/value pairs in this ContentLocatorPart.
        /// </summary>
        /// <returns>an enumerator for the key/value pairs; never returns null</returns>
        public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
        {
            return ((IEnumerable<KeyValuePair<string, string>>)_nameValues).GetEnumerator();
        }
 
        /// <summary>
        ///
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException">key is null</exception>
        public bool TryGetValue(string key, out string value)
        {
            if (key == null)
                throw new ArgumentNullException("key");
 
            return _nameValues.TryGetValue(key, out value);
        }
 
        /// <summary>
        ///
        /// </summary>
        /// <param name="pair"></param>
        /// <exception cref="ArgumentNullException">pair is null</exception>
        void ICollection<KeyValuePair<string, string>>.Add(KeyValuePair<string, string> pair)
        {
            ((ICollection<KeyValuePair<string, string>>)_nameValues).Add(pair);
        }
 
        /// <summary>
        ///
        /// </summary>
        /// <param name="pair"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException">pair is null</exception>
        bool ICollection<KeyValuePair<string, string>>.Contains(KeyValuePair<string, string> pair)
        {
            return ((ICollection<KeyValuePair<string, string>>)_nameValues).Contains(pair);
        }
 
        /// <summary>
        ///
        /// </summary>
        /// <param name="pair"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException">pair is null</exception>
        bool ICollection<KeyValuePair<string, string>>.Remove(KeyValuePair<string, string> pair)
        {
            return ((ICollection<KeyValuePair<string, string>>)_nameValues).Remove(pair);
        }
 
        /// <summary>
        ///
        /// </summary>
        /// <param name="target"></param>
        /// <param name="startIndex"></param>
        /// <exception cref="ArgumentNullException">target is null</exception>
        /// <exception cref="ArgumentOutOfRangeException">startIndex is less than zero or greater than the lenght of target</exception>
        void ICollection<KeyValuePair<string,string>>.CopyTo(KeyValuePair<string, string>[] target, int startIndex)
        {
            if (target == null)
                throw new ArgumentNullException("target");
            if (startIndex < 0 || startIndex > target.Length)
                throw new ArgumentOutOfRangeException("startIndex");
 
            ((ICollection<KeyValuePair<string, string>>)_nameValues).CopyTo(target, startIndex);
        }
 
        #endregion Public Methods
 
        //------------------------------------------------------
        //
        //  Public Operators
        //
        //------------------------------------------------------
        //------------------------------------------------------
        //
        //  Public Events
        //
        //------------------------------------------------------
        //------------------------------------------------------
        //
        //  Public Properties
        //
        //------------------------------------------------------
 
        #region Public Properties
 
        /// <summary>
        ///     The number of name/value pairs in this ContentLocatorPart.
        /// </summary>
        /// <value>count of name/value pairs</value>
        public int Count
        {
            get
            {
                return _nameValues.Count;
            }
        }
 
        /// <summary>
        ///     Indexer provides lookup of values by key.  Gets or sets the value
        ///     in the ContentLocatorPart for the specified key.  If the key does not exist
        ///     in the ContentLocatorPart,
        /// </summary>
        /// <param name="key">key</param>
        /// <returns>the value stored in this locator part for key</returns>
        public string this[string key]
        {
            get
            {
                if (key == null)
                {
                    throw new ArgumentNullException("key");
                }
 
                string value = null;
                _nameValues.TryGetValue(key, out value);
                return value;
            }
            set
            {
                if (key == null)
                {
                    throw new ArgumentNullException("key");
                }
 
                if (value == null)
                {
                    throw new ArgumentNullException("value");
                }
 
                string oldValue = null;
                _nameValues.TryGetValue(key, out oldValue);
 
                // If the new value is actually different, then we add it and fire
                // a change notification
                if ((oldValue == null) || (oldValue != value))
                {
                    _nameValues[key] = value;
                    FireDictionaryChanged();
                }
            }
        }
 
        /// <summary>
        ///
        /// </summary>
        public bool IsReadOnly
        {
            get
            {
                return false;
            }
        }
 
        /// <summary>
        ///     Returns a collection of all the keys in this ContentLocatorPart.
        /// </summary>
        /// <value>keys</value>
        public ICollection<string> Keys
        {
            get
            {
                return _nameValues.Keys;
            }
        }
 
        /// <summary>
        ///     Returns a collection of all the values in this ContentLocatorPart.
        /// </summary>
        /// <value>values</value>
        public ICollection<string> Values
        {
            get
            {
                return _nameValues.Values;
            }
        }
 
        #endregion Public Properties
 
        //------------------------------------------------------
        //
        //  Public Events
        //
        //------------------------------------------------------
 
        //------------------------------------------------------
        //
        //  Internal Methods
        //
        //------------------------------------------------------
        //------------------------------------------------------
        //
        //  Internal Operators
        //
        //------------------------------------------------------
        //------------------------------------------------------
        //
        //  Internal Events
        //
        //------------------------------------------------------
 
        #region Public Events
 
        /// <summary>
        ///
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;
 
        #endregion Public Events
 
        //------------------------------------------------------
        //
        //  Internal Properties
        //
        //------------------------------------------------------
        //------------------------------------------------------
        //
        //  Private Methods
        //
        //------------------------------------------------------
 
        #region Private Methods
 
        /// <summary>
        ///     Notify the owner this ContentLocatorPart has changed.
        /// </summary>
        private void FireDictionaryChanged()
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(null));
            }
        }
 
        #endregion Private Methods
 
        //------------------------------------------------------
        //
        //  Private Fields
        //
        //------------------------------------------------------
 
        #region Private Fields
 
        /// <summary>
        ///     The internal data structure.
        /// </summary>
        private Dictionary<string,string> _nameValues;
 
        #endregion Private Fields
    }
}