/// <summary>
        /// Gets the internal list of class handlers for the specified routed event.
        /// </summary>
        /// <param name="classType">The type of the class for which to retrieve class handlers.</param>
        /// <param name="routedEvent">A <see cref="RoutedEvent"/> which identifies the event for which to retrieve class handlers.</param>
        /// <returns>The internal list of handlers for the specified routed event.</returns>
        internal static List<RoutedEventHandlerMetadata> GetClassHandlers(Type classType, RoutedEvent routedEvent)
        {
            Contract.Require(classType, "classType");
            Contract.Require(routedEvent, "routedEvent");

            var manager = GetClassHandlerManager(routedEvent, classType, false);
            if (manager == null)
            {
                while (true)
                {
                    classType = classType.BaseType;

                    if (classType == null)
                        break;

                    manager = GetClassHandlerManager(routedEvent, classType, false);
                    if (manager != null)
                    {
                        return manager.GetClassHandlers();
                    }
                }
                return null;
            }
            return manager.GetClassHandlers();
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="RoutedEventClassHandlerManager"/> class.
        /// </summary>
        /// <param name="routedEvent">The routed event with which the registry is associated.</param>
        /// <param name="ownerType">The type with which this registry is associated.</param>
        public RoutedEventClassHandlerManager(RoutedEvent routedEvent, Type ownerType)
        {
            Contract.Require(routedEvent, nameof(routedEvent));
            Contract.Require(ownerType, nameof(ownerType));

            this.routedEvent = routedEvent;
            this.ownerType   = ownerType;
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="RoutedEventClassHandlerManager"/> class.
        /// </summary>
        /// <param name="routedEvent">The routed event with which the registry is associated.</param>
        /// <param name="ownerType">The type with which this registry is associated.</param>
        public RoutedEventClassHandlerManager(RoutedEvent routedEvent, Type ownerType)
        {
            Contract.Require(routedEvent, "routedEvent");
            Contract.Require(ownerType, "type");

            this.routedEvent = routedEvent;
            this.ownerType   = ownerType;
        }
Example #4
0
        /// <summary>
        /// Removes a routed event handler from the specified element.
        /// </summary>
        public static void RemoveHandler(DependencyObject dobj, RoutedEvent routedEvent, Delegate handler)
        {
            var element = dobj as UIElement;
            if (element == null)
                throw new ArgumentNullException(PresentationStrings.NotUIElement);

            element.RemoveHandler(routedEvent, handler);
        }
Example #5
0
        /// <summary>
        /// Unregisters the specified subscriber from receiving routed event notifications for the specified routed event.
        /// </summary>
        /// <param name="dobj">The dependency object to monitor for changes.</param>
        /// <param name="routedEvent">The routed event </param>
        /// <param name="subscriber">The subscriber that wishes to stop receiving change notifications for the specified dependency property.</param>
        internal static void UnregisterRaisedNotification(DependencyObject dobj, RoutedEvent routedEvent, IRoutedEventRaisedNotificationSubscriber subscriber)
        {
            Contract.Require(dobj, "dobj");
            Contract.Require(routedEvent, "routedEvent");
            Contract.Require(subscriber, "subscriber");

            routedEvent.raisedNotificationServer.Unsubscribe(dobj, subscriber);
        }
 /// <summary>
 /// Gets the list of registered event handlers for the specified event.
 /// </summary>
 /// <param name="evt">A <see cref="RoutedEvent"/> value which identifies the event from which to retrieve a handler list.</param>
 /// <returns>The manager's internal list of handlers for the specified event.</returns>
 internal List<RoutedEventHandlerMetadata> GetHandlers(RoutedEvent evt)
 {
     List<RoutedEventHandlerMetadata> handlers;
     lock (routedEventDelegates)
     {
         routedEventDelegates.TryGetValue(evt.ID, out handlers);
     }
     return handlers;
 }
        /// <summary>
        /// Registers a new routed event.
        /// </summary>
        /// <param name="name">The routed event's name.</param>
        /// <param name="uvssName">The routed event's name within the UVSS styling system.</param>
        /// <param name="routingStrategy">The routed event's routing strategy.</param>
        /// <param name="delegateType">The routed event's delegate type.</param>
        /// <param name="ownerType">The routed event's owner type.</param>
        /// <returns>A <see cref="RoutedEvent"/> instance which represents the registered routed event.</returns>
        public static RoutedEvent Register(String name, String uvssName, RoutingStrategy routingStrategy, Type delegateType, Type ownerType)
        {
            Contract.Require(name, "name");
            Contract.Require(delegateType, "delegateType");
            Contract.Require(ownerType, "ownerType");

            var evt = new RoutedEvent(reid++, name, uvssName, routingStrategy, delegateType, ownerType);
            RegisterInternal(evt, ownerType);
            return evt;
        }
        /// <summary>
        /// Registers a class handler for a routed event.
        /// </summary>
        /// <param name="classType">The type of the class that is declaring class handling.</param>
        /// <param name="routedEvent">A <see cref="RoutedEvent"/> which identifies the event to handle.</param>
        /// <param name="handler">The delegate that represents the class handler to register.</param>
        /// <param name="handledEventsToo">A value indicating whether to invoke the handler even if it has already been handled.</param>
        public static void RegisterClassHandler(Type classType, RoutedEvent routedEvent, Delegate handler, Boolean handledEventsToo)
        {
            Contract.Require(classType, "classType");
            Contract.Require(routedEvent, "routedEvent");
            Contract.Require(handler, "handler");

            var manager = GetClassHandlerManager(routedEvent, classType);
            manager.AddHandler(handler, handledEventsToo);

            Dictionary<Type, RoutedEventClassHandlerManager> otherManagers;
            if (managers.TryGetValue(routedEvent, out otherManagers))
            {
                foreach (var kvp in otherManagers)
                {
                    if (kvp.Key.IsSubclassOf(classType))
                    {
                        kvp.Value.AddHandler(classType, handler, handledEventsToo);
                    }
                }
            }
        }
        /// <summary>
        /// Creates an invocation delegate for the specified routed event.
        /// </summary>
        /// <param name="evt">A <see cref="RoutedEvent"/> which identifies the routed event for which to create an invocation delegate.</param>
        /// <returns>The invocation delegate for the specified routed event.</returns>
        public static Delegate CreateInvocationDelegate(RoutedEvent evt)
        {
            Contract.Require(evt, nameof(evt));

            if (!IsValidRoutedEventDelegate(evt.DelegateType))
                throw new InvalidOperationException(PresentationStrings.InvalidRoutedEventDelegate.Format(evt.DelegateType.Name));

            switch (evt.RoutingStrategy)
            {
                case RoutingStrategy.Bubble:
                    return CreateInvocationDelegateForBubbleStrategy(evt);

                case RoutingStrategy.Direct:
                    return CreateInvocationDelegateForDirectStrategy(evt);

                case RoutingStrategy.Tunnel:
                    return CreateInvocationDelegateForTunnelStrategy(evt);

                default:
                    throw new InvalidOperationException(PresentationStrings.InvalidRoutingStrategy);
            }
        }
Example #10
0
        /// <summary>
        /// Removes a handler from the specified routed event.
        /// </summary>
        /// <param name="evt">A <see cref="RoutedEvent"/> value which identifies the event from which to remove a handler.</param>
        /// <param name="handler">A delegate which represents the handler to remove from the specified routed event.</param>
        public void Remove(RoutedEvent evt, Delegate handler)
        {
            Contract.Require(evt, nameof(evt));
            Contract.Require(handler, nameof(handler));

            lock (routedEventDelegates)
            {
                List<RoutedEventHandlerMetadata> events;
                if (!routedEventDelegates.TryGetValue(evt.ID, out events))
                    return;

                lock (events)
                {
                    for (int i = 0; i < events.Count; i++)
                    {
                        if (events[i].Handler == handler)
                        {
                            events.RemoveAt(i);
                            return;
                        }
                    }
                }
            }
        }
Example #11
0
        /// <summary>
        /// Adds a handler to the specified routed event.
        /// </summary>
        /// <param name="evt">A <see cref="RoutedEvent"/> value which identifies the event to which to add a handler.</param>
        /// <param name="handler">A delegate which represents the handler to add to the specified routed event.</param>
        /// <param name="handledEventsToo">A value indicating whether the handler should receive events which have already been handled by other handlers.</param>
        public void Add(RoutedEvent evt, Delegate handler, Boolean handledEventsToo)
        {
            Contract.Require(evt, nameof(evt));
            Contract.Require(handler, nameof(handler));

            if (evt.DelegateType != handler.GetType())
                throw new ArgumentException(PresentationStrings.HandlerTypeMismatch.Format(handler.GetType().Name, evt.DelegateType.Name), "handler");

            lock (routedEventDelegates)
            {
                List<RoutedEventHandlerMetadata> events;
                if (!routedEventDelegates.TryGetValue(evt.ID, out events))
                {
                    events = new List<RoutedEventHandlerMetadata>();
                    routedEventDelegates[evt.ID] = events;
                }

                var routedEventInfo = new RoutedEventHandlerMetadata(handler, 0, 0, handledEventsToo);
                lock (events)
                {
                    events.Add(routedEventInfo);
                }
            }
        }
 /// <summary>
 /// Registers a class handler for a routed event.
 /// </summary>
 /// <param name="classType">The type of the class that is declaring class handling.</param>
 /// <param name="routedEvent">A <see cref="RoutedEvent"/> which identifies the event to handle.</param>
 /// <param name="handler">The delegate that represents the class handler to register.</param>
 public static void RegisterClassHandler(Type classType, RoutedEvent routedEvent, Delegate handler)
 {
     RegisterClassHandler(classType, routedEvent, handler, false);
 }
        /// <summary>
        /// Creates a new <see cref="RoutedEventClassHandlerManager"/> for the specified event and type,
        /// populating the manager with relevant handlers from existing types that have already been registered.
        /// </summary>
        /// <param name="routedEvent">The event for which to create a class handler manager.</param>
        /// <param name="classType">The type for which to create a class handler manager.</param>
        /// <returns>The <see cref="RoutedEventClassHandlerManager"/> that was created for the specified event and type.</returns>
        private static RoutedEventClassHandlerManager CreateClassHandlerManager(RoutedEvent routedEvent, Type classType)
        {
            var manager = new RoutedEventClassHandlerManager(routedEvent, classType);

            Dictionary<Type, RoutedEventClassHandlerManager> existing;
            if (managers.TryGetValue(routedEvent, out existing))
            {
                manager.SuspendSort();
                foreach (var kvp in existing)
                {
                    if (classType.IsSubclassOf(kvp.Key))
                    {
                        var handlers = kvp.Value.GetClassHandlers();
                        if (handlers == null)
                            continue;

                        lock (handlers)
                        {
                            foreach (var handler in handlers)
                            {
                                manager.AddHandler(kvp.Key, handler.Handler, handler.HandledEventsToo);
                            }
                        }
                    }
                }
                manager.ResumeSort();
            }

            return manager;
        }
        /// <summary>
        /// Gets the next event handler to invoke for the current element.
        /// </summary>
        /// <param name="element">The element for which event handlers are being invoked.</param>
        /// <param name="evt">A <see cref="RoutedEvent"/> which identifies the routed event for which handlers are being invoked.</param>
        /// <param name="index">The index of the handler to invoke; this value is incremented by one when this method returns.</param>
        /// <param name="handler">The metadata for the handler that corresponds to the specified index within the handler list.</param>
        /// <returns><see langword="true"/> if a handler was retrieved for the specified index; otherwise, <see langword="false"/>.</returns>
        private static Boolean GetEventHandler(DependencyObject element, RoutedEvent evt, ref Int32 index, ref RoutedEventHandlerMetadata handler)
        {
            var indexTemp = index;

            var classHandlers = RoutedEventClassHandlers.GetClassHandlers(element.GetType(), evt);
            if (classHandlers != null)
            {
                lock (classHandlers)
                {
                    if (indexTemp >= 0 && indexTemp < classHandlers.Count)
                    {
                        handler = classHandlers[indexTemp];
                        index++;
                        return true;
                    }
                    indexTemp -= classHandlers.Count;
                }
            }

            var uiElement = element as UIElement;
            if (uiElement != null)
            {
                var instanceHandlers = uiElement.GetHandlers(evt);
                if (instanceHandlers == null)
                    return false;

                lock (instanceHandlers)
                {
                    if (indexTemp >= instanceHandlers.Count)
                    {
                        return false;
                    }
                    handler = instanceHandlers[indexTemp];
                    index++;
                }

                return true;
            }
            else
            {
                return false;
            }
        }
Example #15
0
        /// <summary>
        /// Adds a new owning type to the specified routed event.
        /// </summary>
        /// <param name="routedEvent">The routed event to which to add an owner type.</param>
        /// <param name="ownerType">The owner type to add to the specified routed event.</param>
        internal static RoutedEvent AddOwner(RoutedEvent routedEvent, Type ownerType)
        {
            Contract.Require(routedEvent, nameof(routedEvent));
            Contract.Require(ownerType, nameof(ownerType));

            RegisterInternal(routedEvent, ownerType);

            return routedEvent;
        }
        /// <summary>
        /// Creates an invocation delegate for a routed event which uses the <see cref="RoutingStrategy.Direct"/> routing strategy.
        /// </summary>
        /// <param name="evt">A <see cref="RoutedEvent"/> which identifies the routed event for which to create an invocation delegate.</param>
        /// <returns>The invocation delegate for the specified routed event.</returns>
        private static Delegate CreateInvocationDelegateForDirectStrategy(RoutedEvent evt)
        {
            /* DIRECT STRATEGY
             * The simplest strategy; the event is only invoked on the element that raised it.
             * Our invocation delegate looks something like this:
             *
             * void fn(DependencyObject element, p1, p2, ..., pN, RoutedEventData data)
             * {
             *      var index = 0;
             *      var handler = default(RoutedEventHandlerMetadata);
             *
             *      while (GetEventHandler(element, RoutedEventID, ref index, ref handler))
             *      {
             *          if (ShouldEventBeRaisedForElement(data, handler.HandledEventsToo))
             *          {
             *              handler.Handler(element, p1, p2, ..., pN, data);
             *          }
             *      }
             *
             *      RoutedEventID.RaiseRaisedNotification(element, data);
             *
             *      if (data.AutoRelease)
             *          data.Release();
             * }
             */

            #if CODE_GEN_ENABLED
            var evtInvoke = evt.DelegateType.GetMethod("Invoke");
            var evtParams = evtInvoke.GetParameters().ToArray();

            var expParams = evtParams.Select(x => Expression.Parameter(x.ParameterType, x.Name)).ToList();
            var expParamElement = expParams.First();
            var expParamData = expParams.Last();

            var expParts = new List<Expression>();
            var expVars = new List<ParameterExpression>();

            var varIndex = Expression.Variable(typeof(Int32), "index");
            expVars.Add(varIndex);

            var varHandler = Expression.Variable(typeof(RoutedEventHandlerMetadata), "handlers");
            expVars.Add(varHandler);

            var expInvokeBreak = Expression.Label();
            var expInvoke = Expression.Loop(
                Expression.IfThenElse(
                    Expression.Call(miGetEventHandler, expParamElement, Expression.Constant(evt), varIndex, varHandler),
                    Expression.IfThen(
                        Expression.Call(miShouldEventBeRaisedForElement, expParamData, Expression.Property(varHandler, "HandledEventsToo")),
                        Expression.Invoke(Expression.Convert(Expression.Property(varHandler, "Handler"), evt.DelegateType), expParams)
                    ),
                    Expression.Break(expInvokeBreak)
                ),
                expInvokeBreak
            );
            expParts.Add(expInvoke);

            var expRaiseRaised = Expression.Call(Expression.Constant(evt), miRaiseRaisedNotification, expParamElement, expParamData);
            expParts.Add(expRaiseRaised);

            expParts.Add(Expression.IfThen(Expression.IsTrue(Expression.Property(expParamData, nameof(RoutedEventData.AutoRelease))),
                Expression.Call(expParamData, nameof(RoutedEventData.Release), null)));

            return Expression.Lambda(evt.DelegateType, Expression.Block(expVars, expParts), expParams).Compile();
            #else
            return CreateDelegateForReflectionBasedImplementation(evt, nameof(ReflectionBasedImplementationForDirectStrategy));
            #endif
        }
        /// <summary>
        /// Creates an invocation delegate for a routed event which uses the <see cref="RoutingStrategy.Bubble"/> routing strategy.
        /// </summary>
        /// <param name="evt">A <see cref="RoutedEvent"/> which identifies the routed event for which to create an invocation delegate.</param>
        /// <returns>The invocation delegate for the specified routed event.</returns>
        private static Delegate CreateInvocationDelegateForBubbleStrategy(RoutedEvent evt)
        {
            /* BUBBLE STRATEGY
             * For a given event delegate type TDelegate, we're constructing a method which basically looks like this:
             *
             * void fn(DependencyObject element, p1, p2, ..., pN, RoutedEventData data)
             * {
             *      var index = 0;
             *      var handler = default(RoutedEventHandlerMetadata);
             *      var current = element;
             *
             *      while (ShouldContinueBubbling(element, ref current))
             *      {
             *          index = 0;
             *          while (GetEventHandler(current, RoutedEventID, ref index, ref handler))
             *          {
             *              if (ShouldEventBeRaisedForElement(data, handler.HandledEventsToo))
             *              {
             *                  handler.Handler(current, p1, p2, ..., pN, data);
             *              }
             *          }
             *
             *          RoutedEventID.RaiseRaisedNotification(current, data);
             *      }
             *
             *      if (data.AutoRelease)
             *          data.Release();
             * }
             */

            #if CODE_GEN_ENABLED
            var evtInvoke = evt.DelegateType.GetMethod("Invoke");
            var evtParams = evtInvoke.GetParameters().ToArray();

            var expParams = evtParams.Select(x => Expression.Parameter(x.ParameterType, x.Name)).ToList();
            var expParamElement = expParams.First();
            var expParamData = expParams.Last();

            var expParts = new List<Expression>();
            var expVars = new List<ParameterExpression>();

            var varIndex = Expression.Variable(typeof(Int32), "index");
            expVars.Add(varIndex);

            var varHandler = Expression.Variable(typeof(RoutedEventHandlerMetadata), "handlers");
            expVars.Add(varHandler);

            var varCurrent = Expression.Variable(typeof(DependencyObject), "current");
            expVars.Add(varCurrent);

            var innerEventHandlerParams = new List<ParameterExpression>();
            innerEventHandlerParams.Add(varCurrent);
            innerEventHandlerParams.AddRange(expParams.Skip(1));

            var expWhileBubbleBreakOuter = Expression.Label();
            var expWhileBubbleBreakInner = Expression.Label();

            var expWhileBubble = Expression.Loop(
                Expression.IfThenElse(
                    Expression.Call(miShouldContinueBubbling, expParamElement, varCurrent),
                    Expression.Block(
                        Expression.Assign(varIndex, Expression.Constant(0)),
                        Expression.Loop(
                            Expression.IfThenElse(
                                Expression.Call(miGetEventHandler, varCurrent, Expression.Constant(evt), varIndex, varHandler),
                                Expression.IfThen(
                                    Expression.Call(miShouldEventBeRaisedForElement, expParamData, Expression.Property(varHandler, "HandledEventsToo")),
                                    Expression.Invoke(Expression.Convert(Expression.Property(varHandler, "Handler"), evt.DelegateType), innerEventHandlerParams)
                                ),
                                Expression.Break(expWhileBubbleBreakInner)
                            ),
                            expWhileBubbleBreakInner
                        ),
                        Expression.Call(Expression.Constant(evt), miRaiseRaisedNotification, varCurrent, expParamData)
                    ),
                    Expression.Break(expWhileBubbleBreakOuter)
                ),
                expWhileBubbleBreakOuter
            );
            expParts.Add(expWhileBubble);

            expParts.Add(Expression.IfThen(Expression.IsTrue(Expression.Property(expParamData, nameof(RoutedEventData.AutoRelease))),
                Expression.Call(expParamData, nameof(RoutedEventData.Release), null)));

            return Expression.Lambda(evt.DelegateType, Expression.Block(expVars, expParts), expParams).Compile();
            #else
            return CreateDelegateForReflectionBasedImplementation(evt, nameof(ReflectionBasedImplementationForBubbleStrategy));
            #endif
        }
        /// <summary>
        /// An implementation of the tunnel routing strategy which uses reflection rather than
        /// dynamic runtime code generation.
        /// </summary>
        private static void ReflectionBasedImplementationForTunnelStrategy(RoutedEvent evt, Object[] parameters)
        {
            var index = 0;
            var handler = default(RoutedEventHandlerMetadata);
            var element = (DependencyObject)parameters[0];
            var current = default(DependencyObject);
            var data = (RoutedEventData)parameters[parameters.Length - 1];

            while (ShouldContinueTunnelling(element, ref current))
            {
                index = 0;
                while (GetEventHandler(current, evt, ref index, ref handler))
                {
                    if (ShouldEventBeRaisedForElement(data, handler.HandledEventsToo))
                    {
                        parameters[0] = current;
                        handler.Handler.DynamicInvoke(parameters);
                    }
                }

                evt.RaiseRaisedNotification(current, data);
            }

            if (data.AutoRelease)
                data.Release();
        }
        /// <summary>
        /// Creates a delegate which wraps a reflection-based routing implementation.
        /// </summary>
        private static Delegate CreateDelegateForReflectionBasedImplementation(RoutedEvent evt, String method)
        {
            var evtInvoke = evt.DelegateType.GetMethod("Invoke");
            var evtParams = evtInvoke.GetParameters().ToArray();

            var implMethod = typeof(RoutedEventInvocation).GetMethod(method, BindingFlags.NonPublic | BindingFlags.Static);

            var expParams = evtParams.Select(x => Expression.Parameter(x.ParameterType, x.Name)).ToList();
            var expImplParameters = expParams.Select(x => Expression.Convert(x, typeof(Object)));
            var expImplMethodCall = Expression.Call(implMethod, Expression.Constant(evt),
                Expression.NewArrayInit(typeof(Object), expImplParameters));

            return Expression.Lambda(evt.DelegateType, expImplMethodCall, expParams).Compile();
        }
Example #20
0
        /// <summary>
        /// Registers the specified subscriber to receive routed event notifications for the specified routed event.
        /// </summary>
        /// <param name="dobj">The dependency object to monitor for changes.</param>
        /// <param name="routedEvent">The dependency property for which to receive change notifications.</param>
        /// <param name="subscriber">The subscriber that wishes to receive change notifications for the specified dependency property.</param>
        internal static void RegisterRaisedNotification(DependencyObject dobj, RoutedEvent routedEvent, IRoutedEventRaisedNotificationSubscriber subscriber)
        {
            Contract.Require(dobj, nameof(dobj));
            Contract.Require(routedEvent, nameof(routedEvent));
            Contract.Require(subscriber, nameof(subscriber));

            routedEvent.raisedNotificationServer.Subscribe(dobj, subscriber);
        }
        /// <summary>
        /// Adds a new owning type to the specified routed event.
        /// </summary>
        /// <param name="routedEvent">The routed event to which to add an owner type.</param>
        /// <param name="ownerType">The owner type to add to the specified routed event.</param>
        internal static RoutedEvent AddOwner(RoutedEvent routedEvent, Type ownerType)
        {
            Contract.Require(routedEvent, "routedEvent");
            Contract.Require(ownerType, "ownerType");

            RegisterInternal(routedEvent, ownerType);

            return routedEvent;
        }
        /// <summary>
        /// Gets the <see cref="RoutedEventClassHandlerManager"/> for the specified event and type.
        /// </summary>
        /// <param name="routedEvent">The event for which to retrieve a class handler manager.</param>
        /// <param name="classType">The type for which to retrieve a class handler manager.</param>
        /// <param name="createIfMissing">A value specifying whether the manager should be created if it does not already exist.</param>
        /// <returns>The <see cref="RoutedEventClassHandlerManager"/> for the specified event and type.</returns>
        private static RoutedEventClassHandlerManager GetClassHandlerManager(RoutedEvent routedEvent, Type classType, Boolean createIfMissing = true)
        {
            Dictionary<Type, RoutedEventClassHandlerManager> managersByType;
            if (!managers.TryGetValue(routedEvent, out managersByType))
            {
                if (!createIfMissing)
                    return null;

                managersByType = new Dictionary<Type, RoutedEventClassHandlerManager>();
                managers[routedEvent] = managersByType;
            }

            RoutedEventClassHandlerManager manager;
            if (!managersByType.TryGetValue(classType, out manager))
            {
                if (!createIfMissing)
                    return null;

                manager = CreateClassHandlerManager(routedEvent, classType);
                managersByType[classType] = manager;
            }

            return manager;
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="RoutedEventRaisedNotificationServer"/> class.
        /// </summary>
        /// <param name="routedEvent">The routed event that this notification server represents.</param>
        public RoutedEventRaisedNotificationServer(RoutedEvent routedEvent)
        {
            Contract.Require(routedEvent, nameof(routedEvent));

            this.routedEvent = routedEvent;
        }
Example #24
0
        /// <summary>
        /// Registers the specified routed event.
        /// </summary>
        /// <param name="evt">The routed event to register.</param>
        /// <param name="ownerType">The owner type for which to register the routed event.</param>
        private static void RegisterInternal(RoutedEvent evt, Type ownerType)
        {
            var propertyDomain = GetEventDomain(ownerType);
            if (propertyDomain.ContainsKey(evt.Name))
                throw new ArgumentException(PresentationStrings.RoutedEventAlreadyRegistered);

            propertyDomain[evt.Name] = evt;

            var stylingPropertyDomain = GetStylingEventDomain(ownerType);
            if (stylingPropertyDomain.ContainsKey(evt.UvssName))
                throw new ArgumentException(PresentationStrings.RoutedEventAlreadyRegistered);

            stylingPropertyDomain[evt.UvssName] = evt;
        }
        /// <summary>
        /// Creates an invocation delegate for a routed event which uses the <see cref="RoutingStrategy.Tunnel"/> routing strategy.
        /// </summary>
        /// <param name="evt">A <see cref="RoutedEvent"/> which identifies the routed event for which to create an invocation delegate.</param>
        /// <returns>The invocation delegate for the specified routed event.</returns>
        private static Delegate CreateInvocationDelegateForTunnelStrategy(RoutedEvent evt)
        {
            /* TUNNEL STRATEGY
             * Basically the opposite of the bubble strategy; we start at the root of the tree and work down.
             * Note that ShouldContinueTunnelling() builds a stack representing the path to take on the first call.
             *
             * void fn(DependencyObject element, p1, p2, ..., pN, RoutedEventData data)
             * {
             *      var index    = 0;
             *      var current  = default(DependencyObject);
             *      var handlers = default(List<RoutedEventHandlerMetadata>);
             *
             *      while (ShouldContinueTunnelling(element, ref current))
             *      {
             *          index = 0;
             *          while (GetEventHandler(current, RoutedEventID, index, out handler))
             *          {
             *              if (ShouldEventBeRaisedForElement(data, handler.HandledEventsToo))
             *              {
             *                  handler.Handler(current, p1, p2, ..., pN, data);
             *              }
             *          }
             *
             *          RoutedEventID.RaiseRaisedNotification(current, data);
             *      }
             *
             *      if (data.AutoRelease)
             *          data.Release();
             * }
             */

            #if CODE_GEN_ENABLED
            var evtInvoke = evt.DelegateType.GetMethod("Invoke");
            var evtParams = evtInvoke.GetParameters().ToArray();

            var expParams = evtParams.Select(x => Expression.Parameter(x.ParameterType, x.Name)).ToList();
            var expParamElement = expParams.First();
            var expParamData = expParams.Last();

            var expParts = new List<Expression>();
            var expVars = new List<ParameterExpression>();

            var varIndex = Expression.Variable(typeof(Int32), "index");
            expVars.Add(varIndex);

            var varHandler = Expression.Variable(typeof(RoutedEventHandlerMetadata), "handler");
            expVars.Add(varHandler);

            var varCurrent = Expression.Variable(typeof(DependencyObject), "current");
            expVars.Add(varCurrent);

            var innerEventHandlerParams = new List<ParameterExpression>();
            innerEventHandlerParams.Add(varCurrent);
            innerEventHandlerParams.AddRange(expParams.Skip(1));

            var expWhileTunnelBreakOuter = Expression.Label();
            var expWhileTunnelBreakInner = Expression.Label();

            var expWhileTunnel = Expression.Loop(
                Expression.IfThenElse(
                    Expression.Call(miShouldContinueTunnelling, expParamElement, varCurrent),
                    Expression.Block(
                        Expression.Assign(varIndex, Expression.Constant(0)),
                        Expression.Loop(
                            Expression.IfThenElse(
                                Expression.Call(miGetEventHandler, varCurrent, Expression.Constant(evt), varIndex, varHandler),
                                Expression.IfThen(
                                    Expression.Call(miShouldEventBeRaisedForElement, expParamData, Expression.Property(varHandler, "HandledEventsToo")),
                                    Expression.Invoke(Expression.Convert(Expression.Property(varHandler, "Handler"), evt.DelegateType), innerEventHandlerParams)
                                ),
                                Expression.Break(expWhileTunnelBreakInner)
                            ),
                            expWhileTunnelBreakInner
                        ),
                        Expression.Call(Expression.Constant(evt), miRaiseRaisedNotification, varCurrent, expParamData)
                    ),
                    Expression.Break(expWhileTunnelBreakOuter)
                ),
                expWhileTunnelBreakOuter
            );
            expParts.Add(expWhileTunnel);

            expParts.Add(Expression.IfThen(Expression.IsTrue(Expression.Property(expParamData, nameof(RoutedEventData.AutoRelease))),
                Expression.Call(expParamData, nameof(RoutedEventData.Release), null)));

            return Expression.Lambda(evt.DelegateType, Expression.Block(expVars, expParts), expParams).Compile();
            #else
            return CreateDelegateForReflectionBasedImplementation(evt, nameof(ReflectionBasedImplementationForTunnelStrategy));
            #endif
        }