/// <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; }
/// <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); }
/// <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); } }
/// <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; } } } } }
/// <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; } }
/// <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(); }
/// <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; }
/// <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 }