File: System\Windows\EventHandlersStore.cs
Web Access
Project: src\src\Microsoft.DotNet.Wpf\src\PresentationCore\PresentationCore.csproj (PresentationCore)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
 
using MS.Utility;
 
using SR=MS.Internal.PresentationCore.SR;
 
namespace System.Windows
{
    /// <summary>
    ///     Container for the event handlers
    /// </summary>
    /// <remarks>
    ///     EventHandlersStore is a hashtable 
    ///     of handlers for a given 
    ///     EventPrivateKey or RoutedEvent
    /// </remarks>
    internal class EventHandlersStore
    {
        #region Construction
        
        /// <summary>
        ///     Constructor for EventHandlersStore
        /// </summary>
        public EventHandlersStore()
        {
            _entries = new FrugalMap();
        }
 
        /// <summary>
        /// Copy constructor for EventHandlersStore
        /// </summary>
        public EventHandlersStore(EventHandlersStore source)
        {
            _entries = source._entries;
        }
        
        #endregion Construction
 
        #region ExternalAPI
 
        /// <summary>
        ///     Adds a Clr event handler for the 
        ///     given EventPrivateKey to the store
        /// </summary>
        /// <param name="key">
        ///     Private key for the event
        /// </param>
        /// <param name="handler">
        ///     Event handler
        /// </param>
        public void Add(EventPrivateKey key, Delegate handler)
        {
            ArgumentNullException.ThrowIfNull(key);
            ArgumentNullException.ThrowIfNull(handler);
 
            // Get the entry corresponding to the given key
            Delegate existingDelegate = (Delegate)this[key];
 
            if (existingDelegate == null)
            {
                _entries[key.GlobalIndex] = handler;
            }
            else
            {
                _entries[key.GlobalIndex] = Delegate.Combine(existingDelegate, handler);
            }
        }
 
 
        /// <summary>
        ///     Removes an instance of the specified 
        ///     Clr event handler for the given 
        ///     EventPrivateKey from the store
        /// </summary>
        /// <param name="key">
        ///     Private key for the event
        /// </param>
        /// <param name="handler">
        ///     Event handler
        /// </param>
        /// <remarks>
        ///     NOTE: This method does nothing if no 
        ///     matching handler instances are found 
        ///     in the store
        /// </remarks>
        public void Remove(EventPrivateKey key, Delegate handler)
        {
            ArgumentNullException.ThrowIfNull(key);
            ArgumentNullException.ThrowIfNull(handler);
 
            // Get the entry corresponding to the given key
            Delegate existingDelegate = (Delegate) this[key];
            if (existingDelegate != null)
            {
                existingDelegate = Delegate.Remove(existingDelegate, handler);
                if (existingDelegate == null)
                {
                    // last handler for this event was removed -- reclaim space in
                    // underlying FrugalMap by setting value to DependencyProperty.UnsetValue
                    _entries[key.GlobalIndex] = DependencyProperty.UnsetValue;
                }
                else
                {
                    _entries[key.GlobalIndex] = existingDelegate;
                }            
            }
        }
 
        /// <summary>
        ///     Gets all the handlers for the given EventPrivateKey
        /// </summary>
        /// <param name="key">
        ///     Private key for the event
        /// </param>
        /// <returns>
        ///     Combined delegate or null if no match found
        /// </returns>
        /// <remarks>
        ///     This method is not exposing a security risk for the reason 
        ///     that the EventPrivateKey for the events will themselves be 
        ///     private to the declaring class. This will be enforced via fxcop rules.
        /// </remarks>
        public Delegate Get(EventPrivateKey key)
        {
            ArgumentNullException.ThrowIfNull(key);
 
            // Return the handlers corresponding to the given key
            return (Delegate)this[key];
        }
        
        #endregion ExternalAPI
        
        #region Operations
 
        /// <summary>
        ///     Adds a routed event handler for the given 
        ///     RoutedEvent to the store
        /// </summary>
        public void AddRoutedEventHandler(
            RoutedEvent routedEvent,
            Delegate handler,
            bool handledEventsToo)
        {
            ArgumentNullException.ThrowIfNull(routedEvent);
            ArgumentNullException.ThrowIfNull(handler);
            if (!routedEvent.IsLegalHandler(handler))
            {
                throw new ArgumentException(SR.HandlerTypeIllegal);
            }
            
            // Create a new RoutedEventHandler
            RoutedEventHandlerInfo routedEventHandlerInfo = 
                new RoutedEventHandlerInfo(handler, handledEventsToo);
 
            // Get the entry corresponding to the given RoutedEvent
            FrugalObjectList<RoutedEventHandlerInfo> handlers = (FrugalObjectList<RoutedEventHandlerInfo>)this[routedEvent];
            if (handlers == null)
            {
                _entries[routedEvent.GlobalIndex] = handlers = new FrugalObjectList<RoutedEventHandlerInfo>(1);
            }
 
            // Add the RoutedEventHandlerInfo to the list
            handlers.Add(routedEventHandlerInfo);
        }
 
        /// <summary>
        ///     Removes an instance of the specified 
        ///     routed event handler for the given 
        ///     RoutedEvent from the store
        /// </summary>
        /// <remarks>
        ///     NOTE: This method does nothing if no 
        ///     matching handler instances are found 
        ///     in the store
        /// </remarks>
        public void RemoveRoutedEventHandler(RoutedEvent routedEvent, Delegate handler)
        {
            ArgumentNullException.ThrowIfNull(routedEvent);
            ArgumentNullException.ThrowIfNull(handler);
            if (!routedEvent.IsLegalHandler(handler))
            {
                throw new ArgumentException(SR.HandlerTypeIllegal);
            }
            
            // Get the entry corresponding to the given RoutedEvent
            FrugalObjectList<RoutedEventHandlerInfo> handlers = (FrugalObjectList<RoutedEventHandlerInfo>)this[routedEvent];
            if (handlers != null && handlers.Count > 0)
            {
                if ((handlers.Count == 1) && (handlers[0].Handler == handler))
                {
                    // this is the only handler for this event and it's being removed
                    // reclaim space in underlying FrugalMap by setting value to
                    // DependencyProperty.UnsetValue
                    _entries[routedEvent.GlobalIndex] = DependencyProperty.UnsetValue;
                }
                else
                {
                    // When a matching instance is found remove it
                    for (int i = 0; i < handlers.Count; i++)
                    {
                        if (handlers[i].Handler == handler)
                        {
                            handlers.RemoveAt(i);
                            break;
                        }
                    }
                }
            }
        }
 
 
        /// <summary>
        ///     Determines whether the given
        ///     RoutedEvent exists in the store.
        /// </summary>
        /// <param name="routedEvent">
        ///     the RoutedEvent of the event.
        /// </param>
 
        public bool Contains(RoutedEvent routedEvent)
        {
            ArgumentNullException.ThrowIfNull(routedEvent);
 
            FrugalObjectList<RoutedEventHandlerInfo> handlers = (FrugalObjectList<RoutedEventHandlerInfo>)this[routedEvent];
 
            return handlers != null && handlers.Count != 0;
        }
 
        /// <summary>
        ///     Get all the event handlers in this store for the given routed event
        /// </summary>
        public RoutedEventHandlerInfo[] GetRoutedEventHandlers(RoutedEvent routedEvent)
        {
            ArgumentNullException.ThrowIfNull(routedEvent);
 
            FrugalObjectList<RoutedEventHandlerInfo> handlers = this[routedEvent];
            if (handlers != null)
            {
                return handlers.ToArray();
            }
 
            return null;
        }
 
        // Returns Handlers for the given key
        internal FrugalObjectList<RoutedEventHandlerInfo> this[RoutedEvent key]
        {            
            get
            {
                Debug.Assert(key != null, "Search key cannot be null");
 
                object list = _entries[key.GlobalIndex];
                if (list == DependencyProperty.UnsetValue)
                {
                    return null;
                }
                else
                {
                    return (FrugalObjectList<RoutedEventHandlerInfo>)list;
                }
            }
        }
 
        internal Delegate this[EventPrivateKey key]
        {
            get
            {
                Debug.Assert(key != null, "Search key cannot be null");
 
                object existingDelegate = _entries[key.GlobalIndex];
                if (existingDelegate == DependencyProperty.UnsetValue)
                {
                    return null;
                }
                else
                {
                    return (Delegate)existingDelegate;
                }
            }
        }
 
        internal int Count
        {
            get
            {
                return _entries.Count;
            }
        }
        
        #endregion Operations
 
        #region Data
 
        // Map of EventPrivateKey/RoutedEvent to Delegate/FrugalObjectList<RoutedEventHandlerInfo> (respectively)
        private FrugalMap _entries;
        
        #endregion Data
    }
}