From 78901c776e152eef908214bb03210fd4a41dc934 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 5 Feb 2015 22:28:33 +0100 Subject: [PATCH] Make class handlers work again. --- Perspex.Controls/Primitives/Thumb.cs | 6 +- Perspex.Input/InputElement.cs | 20 ++-- .../InteractiveTests.cs | 96 ++++++++++++++++++- Perspex.Interactivity/EventSubscription.cs | 17 ++++ Perspex.Interactivity/Interactive.cs | 26 ++--- .../Perspex.Interactivity.csproj | 5 +- Perspex.Interactivity/RoutedEvent.cs | 45 ++++++--- 7 files changed, 168 insertions(+), 47 deletions(-) create mode 100644 Perspex.Interactivity/EventSubscription.cs diff --git a/Perspex.Controls/Primitives/Thumb.cs b/Perspex.Controls/Primitives/Thumb.cs index 64ffaa7535..bd3d15346e 100644 --- a/Perspex.Controls/Primitives/Thumb.cs +++ b/Perspex.Controls/Primitives/Thumb.cs @@ -25,9 +25,9 @@ namespace Perspex.Controls.Primitives static Thumb() { - DragStartedEvent.AddClassHandler(x => x.OnDragStarted); - DragDeltaEvent.AddClassHandler(x => x.OnDragDelta); - DragCompletedEvent.AddClassHandler(x => x.OnDragCompleted); + DragStartedEvent.AddClassHandler(x => x.OnDragStarted, RoutingStrategies.Bubble); + DragDeltaEvent.AddClassHandler(x => x.OnDragDelta, RoutingStrategies.Bubble); + DragCompletedEvent.AddClassHandler(x => x.OnDragCompleted, RoutingStrategies.Bubble); } public event EventHandler DragStarted diff --git a/Perspex.Input/InputElement.cs b/Perspex.Input/InputElement.cs index 7730b6eadd..e186728861 100644 --- a/Perspex.Input/InputElement.cs +++ b/Perspex.Input/InputElement.cs @@ -66,16 +66,16 @@ namespace Perspex.Input { IsEnabledProperty.Changed.Subscribe(IsEnabledChanged); - GotFocusEvent.AddClassHandler(x => x.OnGotFocus); - LostFocusEvent.AddClassHandler(x => x.OnLostFocus); - KeyDownEvent.AddClassHandler(x => x.OnKeyDown); - PreviewKeyDownEvent.AddClassHandler(x => x.OnPreviewKeyDown); - PointerEnterEvent.AddClassHandler(x => x.OnPointerEnter); - PointerLeaveEvent.AddClassHandler(x => x.OnPointerLeave); - PointerMovedEvent.AddClassHandler(x => x.OnPointerMoved); - PointerPressedEvent.AddClassHandler(x => x.OnPointerPressed); - PointerReleasedEvent.AddClassHandler(x => x.OnPointerReleased); - PointerWheelChangedEvent.AddClassHandler(x => x.OnPointerWheelChanged); + GotFocusEvent.AddClassHandler(x => x.OnGotFocus, RoutingStrategies.Bubble); + LostFocusEvent.AddClassHandler(x => x.OnLostFocus, RoutingStrategies.Bubble); + KeyDownEvent.AddClassHandler(x => x.OnKeyDown, RoutingStrategies.Bubble); + PreviewKeyDownEvent.AddClassHandler(x => x.OnPreviewKeyDown, RoutingStrategies.Tunnel); + PointerEnterEvent.AddClassHandler(x => x.OnPointerEnter, RoutingStrategies.Direct); + PointerLeaveEvent.AddClassHandler(x => x.OnPointerLeave, RoutingStrategies.Direct); + PointerMovedEvent.AddClassHandler(x => x.OnPointerMoved, RoutingStrategies.Bubble); + PointerPressedEvent.AddClassHandler(x => x.OnPointerPressed, RoutingStrategies.Bubble); + PointerReleasedEvent.AddClassHandler(x => x.OnPointerReleased, RoutingStrategies.Bubble); + PointerWheelChangedEvent.AddClassHandler(x => x.OnPointerWheelChanged, RoutingStrategies.Bubble); } public event EventHandler GotFocus diff --git a/Perspex.Interactive.UnitTests/InteractiveTests.cs b/Perspex.Interactive.UnitTests/InteractiveTests.cs index 659ee622df..0c45fe46fb 100644 --- a/Perspex.Interactive.UnitTests/InteractiveTests.cs +++ b/Perspex.Interactive.UnitTests/InteractiveTests.cs @@ -216,6 +216,88 @@ namespace Perspex.Interactive.UnitTests Assert.Equal(new[] { "1", "2b", "2b", "1" }, invoked); } + [Fact] + public void Direct_Class_Handlers_Should_Be_Called() + { + var ev = new RoutedEvent( + "test", + RoutingStrategies.Direct, + typeof(RoutedEventArgs), + typeof(TestInteractive)); + var invoked = new List(); + EventHandler handler = (s, e) => invoked.Add(((TestInteractive)s).Id); + + var target = this.CreateTree(ev, null, 0); + + ev.AddClassHandler(typeof(TestInteractive), handler, RoutingStrategies.Direct); + + var args = new RoutedEventArgs(ev, target); + target.RaiseEvent(args); + + Assert.Equal(new[] { "2b" }, invoked); + } + + [Fact] + public void Tunneling_Class_Handlers_Should_Be_Called() + { + var ev = new RoutedEvent( + "test", + RoutingStrategies.Bubble | RoutingStrategies.Tunnel, + typeof(RoutedEventArgs), + typeof(TestInteractive)); + var invoked = new List(); + EventHandler handler = (s, e) => invoked.Add(((TestInteractive)s).Id); + + var target = this.CreateTree(ev, null, 0); + + ev.AddClassHandler(typeof(TestInteractive), handler, RoutingStrategies.Tunnel); + + var args = new RoutedEventArgs(ev, target); + target.RaiseEvent(args); + + Assert.Equal(new[] { "1", "2b" }, invoked); + } + + [Fact] + public void Bubbling_Class_Handlers_Should_Be_Called() + { + var ev = new RoutedEvent( + "test", + RoutingStrategies.Bubble | RoutingStrategies.Tunnel, + typeof(RoutedEventArgs), + typeof(TestInteractive)); + var invoked = new List(); + EventHandler handler = (s, e) => invoked.Add(((TestInteractive)s).Id); + + var target = this.CreateTree(ev, null, 0); + + ev.AddClassHandler(typeof(TestInteractive), handler, RoutingStrategies.Bubble); + + var args = new RoutedEventArgs(ev, target); + target.RaiseEvent(args); + + Assert.Equal(new[] { "2b", "1" }, invoked); + } + + [Fact] + public void Typed_Class_Handlers_Should_Be_Called() + { + var ev = new RoutedEvent( + "test", + RoutingStrategies.Bubble | RoutingStrategies.Tunnel, + typeof(TestInteractive)); + + var target = this.CreateTree(ev, null, 0); + + ev.AddClassHandler(x => x.ClassHandler, RoutingStrategies.Bubble); + + var args = new RoutedEventArgs(ev, target); + target.RaiseEvent(args); + + Assert.True(target.ClassHandlerInvoked); + Assert.True(target.GetVisualParent().ClassHandlerInvoked); + } + private TestInteractive CreateTree( RoutedEvent ev, EventHandler handler, @@ -247,9 +329,12 @@ namespace Perspex.Interactive.UnitTests } }; - foreach (var i in tree.GetSelfAndVisualDescendents().Cast()) + if (handler != null) { - i.AddHandler(ev, handler, handlerRoutes, handledEventsToo); + foreach (var i in tree.GetSelfAndVisualDescendents().Cast()) + { + i.AddHandler(ev, handler, handlerRoutes, handledEventsToo); + } } return target; @@ -259,6 +344,8 @@ namespace Perspex.Interactive.UnitTests { public string Id { get; set; } + public bool ClassHandlerInvoked { get; private set; } + public IEnumerable Children { get @@ -271,6 +358,11 @@ namespace Perspex.Interactive.UnitTests this.AddVisualChildren(value.Cast()); } } + + public void ClassHandler(RoutedEventArgs e) + { + this.ClassHandlerInvoked = true; + } } } } diff --git a/Perspex.Interactivity/EventSubscription.cs b/Perspex.Interactivity/EventSubscription.cs new file mode 100644 index 0000000000..8cb0a61280 --- /dev/null +++ b/Perspex.Interactivity/EventSubscription.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Perspex.Interactivity +{ + internal class EventSubscription + { + public Delegate Handler { get; set; } + + public RoutingStrategies Routes { get; set; } + + public bool AlsoIfHandled { get; set; } + } +} diff --git a/Perspex.Interactivity/Interactive.cs b/Perspex.Interactivity/Interactive.cs index c8dc22053d..df39aa0d39 100644 --- a/Perspex.Interactivity/Interactive.cs +++ b/Perspex.Interactivity/Interactive.cs @@ -16,8 +16,8 @@ namespace Perspex.Interactivity public class Interactive : Layoutable, IInteractive { - private Dictionary> eventHandlers = - new Dictionary>(); + private Dictionary> eventHandlers = + new Dictionary>(); public void AddHandler( RoutedEvent routedEvent, @@ -28,15 +28,15 @@ namespace Perspex.Interactivity Contract.Requires(routedEvent != null); Contract.Requires(handler != null); - List subscriptions; + List subscriptions; if (!this.eventHandlers.TryGetValue(routedEvent, out subscriptions)) { - subscriptions = new List(); + subscriptions = new List(); this.eventHandlers.Add(routedEvent, subscriptions); } - subscriptions.Add(new Subscription + subscriptions.Add(new EventSubscription { Handler = handler, Routes = routes, @@ -58,7 +58,7 @@ namespace Perspex.Interactivity Contract.Requires(routedEvent != null); Contract.Requires(handler != null); - List subscriptions; + List subscriptions; if (this.eventHandlers.TryGetValue(routedEvent, out subscriptions)) { @@ -76,6 +76,7 @@ namespace Perspex.Interactivity if (e.RoutedEvent.RoutingStrategies == RoutingStrategies.Direct) { e.Route = RoutingStrategies.Direct; + e.RoutedEvent.InvokeClassHandlers(this, e); this.RaiseEventImpl(e); } @@ -118,7 +119,9 @@ namespace Perspex.Interactivity { Contract.Requires(e != null); - List subscriptions; + e.RoutedEvent.InvokeClassHandlers(this, e); + + List subscriptions; if (this.eventHandlers.TryGetValue(e.RoutedEvent, out subscriptions)) { @@ -136,14 +139,5 @@ namespace Perspex.Interactivity } } } - - private class Subscription - { - public Delegate Handler { get; set; } - - public RoutingStrategies Routes { get; set; } - - public bool AlsoIfHandled { get; set; } - } } } diff --git a/Perspex.Interactivity/Perspex.Interactivity.csproj b/Perspex.Interactivity/Perspex.Interactivity.csproj index b8faffa4c6..b50018f3a2 100644 --- a/Perspex.Interactivity/Perspex.Interactivity.csproj +++ b/Perspex.Interactivity/Perspex.Interactivity.csproj @@ -8,8 +8,8 @@ {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B} Library Properties - Perspex - Perspex.Interactive + Perspex.Interactivity + Perspex.Interactivity en-US 512 {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} @@ -58,6 +58,7 @@ + diff --git a/Perspex.Interactivity/RoutedEvent.cs b/Perspex.Interactivity/RoutedEvent.cs index 5be4681433..6bf85a294c 100644 --- a/Perspex.Interactivity/RoutedEvent.cs +++ b/Perspex.Interactivity/RoutedEvent.cs @@ -7,6 +7,7 @@ namespace Perspex.Interactivity { using System; + using System.Collections.Generic; using System.Linq.Expressions; using System.Reflection; @@ -20,6 +21,8 @@ namespace Perspex.Interactivity public class RoutedEvent { + private List subscriptions = new List(); + public RoutedEvent( string name, RoutingStrategies routingStrategies, @@ -86,13 +89,33 @@ namespace Perspex.Interactivity return new RoutedEvent(name, routingStrategy, ownerType); } - internal void InvokeRaised(object sender, RoutedEventArgs e) + public void AddClassHandler(Type type, EventHandler handler, RoutingStrategies routes) { - if (this.Raised != null) + this.subscriptions.Add(new ClassEventSubscription { - this.Raised(sender, e); + TargetType = type, + Handler = handler, + Routes = routes, + }); + } + + internal void InvokeClassHandlers(object sender, RoutedEventArgs e) + { + foreach (var sub in this.subscriptions) + { + if (sub.TargetType.GetTypeInfo().IsAssignableFrom(sender.GetType().GetTypeInfo()) && + (e.Route == RoutingStrategies.Direct && sub.Routes == RoutingStrategies.Direct) || + (e.Route != RoutingStrategies.Direct && (e.Route & sub.Routes) != 0)) + { + sub.Handler.DynamicInvoke(sender, e); + } } } + + private class ClassEventSubscription : EventSubscription + { + public Type TargetType { get; set; } + } } public class RoutedEvent : RoutedEvent @@ -106,18 +129,12 @@ namespace Perspex.Interactivity Contract.Requires(typeof(IInteractive).GetTypeInfo().IsAssignableFrom(ownerType.GetTypeInfo())); } - public void AddClassHandler(Func> handler) where TTarget : class + public void AddClassHandler( + Func> handler, + RoutingStrategies routes) where TTarget : class { - this.Raised += (s, e) => - { - var target = s as TTarget; - var args = e as TEventArgs; - - if (target != null) - { - handler(target)(args); - } - }; + var adaptor = (EventHandler)((s, e) => handler((TTarget)s)((TEventArgs)e)); + this.AddClassHandler(typeof(TTarget), adaptor, routes); } } }