diff --git a/src/Avalonia.Interactivity/Interactive.cs b/src/Avalonia.Interactivity/Interactive.cs index f8d388ec89..27ece25183 100644 --- a/src/Avalonia.Interactivity/Interactive.cs +++ b/src/Avalonia.Interactivity/Interactive.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.CompilerServices; using Avalonia.Layout; using Avalonia.VisualTree; @@ -170,10 +171,9 @@ namespace Avalonia.Interactivity e.Route = RoutingStrategies.Bubble; - foreach (var target in this.GetBubbleEventRoute()) - { - ((Interactive)target).RaiseEventImpl(e); - } + var traverser = HierarchyTraverser.Create(e); + + traverser.Traverse(this); } /// @@ -186,10 +186,9 @@ namespace Avalonia.Interactivity e.Route = RoutingStrategies.Tunnel; - foreach (var target in this.GetTunnelEventRoute()) - { - ((Interactive)target).RaiseEventImpl(e); - } + var traverser = HierarchyTraverser.Create(e); + + traverser.Traverse(this); } /// @@ -262,5 +261,67 @@ namespace Avalonia.Interactivity _subscriptions.Remove(_subscription); } } + + private interface ITraverse + { + void Execute(IInteractive target, RoutedEventArgs e); + } + + private struct NopTraverse : ITraverse + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Execute(IInteractive target, RoutedEventArgs e) + { + } + } + + private struct RaiseEventTraverse : ITraverse + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Execute(IInteractive target, RoutedEventArgs e) + { + ((Interactive)target).RaiseEventImpl(e); + } + } + + /// + /// Traverses interactive hierarchy allowing for raising events. + /// + /// Called before parent is traversed. + /// Called after parent has been traversed. + private struct HierarchyTraverser + where TPreTraverse : struct, ITraverse + where TPostTraverse : struct, ITraverse + { + private TPreTraverse _preTraverse; + private TPostTraverse _postTraverse; + private readonly RoutedEventArgs _args; + + private HierarchyTraverser(TPreTraverse preTraverse, TPostTraverse postTraverse, RoutedEventArgs args) + { + _preTraverse = preTraverse; + _postTraverse = postTraverse; + _args = args; + } + + public static HierarchyTraverser Create(RoutedEventArgs args) + { + return new HierarchyTraverser(new TPreTraverse(), new TPostTraverse(), args); + } + + public void Traverse(IInteractive target) + { + _preTraverse.Execute(target, _args); + + IInteractive parent = target.InteractiveParent; + + if (parent != null) + { + Traverse(parent); + } + + _postTraverse.Execute(target, _args); + } + } } } diff --git a/src/Avalonia.Interactivity/InteractiveExtensions.cs b/src/Avalonia.Interactivity/InteractiveExtensions.cs index b03590951b..07e4029240 100644 --- a/src/Avalonia.Interactivity/InteractiveExtensions.cs +++ b/src/Avalonia.Interactivity/InteractiveExtensions.cs @@ -36,29 +36,5 @@ namespace Avalonia.Interactivity routes, handledEventsToo)); } - - /// - /// Gets the route for bubbling events from the specified interactive. - /// - /// The interactive. - /// The event route. - internal static IEnumerable GetBubbleEventRoute(this IInteractive interactive) - { - while (interactive != null) - { - yield return interactive; - interactive = interactive.InteractiveParent; - } - } - - /// - /// Gets the route for tunneling events from the specified interactive. - /// - /// The interactive. - /// The event route. - internal static IEnumerable GetTunnelEventRoute(this IInteractive interactive) - { - return interactive.GetBubbleEventRoute().Reverse(); - } } }