Browse Source

Make class handlers work again.

pull/39/head
Steven Kirk 12 years ago
parent
commit
78901c776e
  1. 6
      Perspex.Controls/Primitives/Thumb.cs
  2. 20
      Perspex.Input/InputElement.cs
  3. 96
      Perspex.Interactive.UnitTests/InteractiveTests.cs
  4. 17
      Perspex.Interactivity/EventSubscription.cs
  5. 26
      Perspex.Interactivity/Interactive.cs
  6. 5
      Perspex.Interactivity/Perspex.Interactivity.csproj
  7. 45
      Perspex.Interactivity/RoutedEvent.cs

6
Perspex.Controls/Primitives/Thumb.cs

@ -25,9 +25,9 @@ namespace Perspex.Controls.Primitives
static Thumb()
{
DragStartedEvent.AddClassHandler<Thumb>(x => x.OnDragStarted);
DragDeltaEvent.AddClassHandler<Thumb>(x => x.OnDragDelta);
DragCompletedEvent.AddClassHandler<Thumb>(x => x.OnDragCompleted);
DragStartedEvent.AddClassHandler<Thumb>(x => x.OnDragStarted, RoutingStrategies.Bubble);
DragDeltaEvent.AddClassHandler<Thumb>(x => x.OnDragDelta, RoutingStrategies.Bubble);
DragCompletedEvent.AddClassHandler<Thumb>(x => x.OnDragCompleted, RoutingStrategies.Bubble);
}
public event EventHandler<VectorEventArgs> DragStarted

20
Perspex.Input/InputElement.cs

@ -66,16 +66,16 @@ namespace Perspex.Input
{
IsEnabledProperty.Changed.Subscribe(IsEnabledChanged);
GotFocusEvent.AddClassHandler<InputElement>(x => x.OnGotFocus);
LostFocusEvent.AddClassHandler<InputElement>(x => x.OnLostFocus);
KeyDownEvent.AddClassHandler<InputElement>(x => x.OnKeyDown);
PreviewKeyDownEvent.AddClassHandler<InputElement>(x => x.OnPreviewKeyDown);
PointerEnterEvent.AddClassHandler<InputElement>(x => x.OnPointerEnter);
PointerLeaveEvent.AddClassHandler<InputElement>(x => x.OnPointerLeave);
PointerMovedEvent.AddClassHandler<InputElement>(x => x.OnPointerMoved);
PointerPressedEvent.AddClassHandler<InputElement>(x => x.OnPointerPressed);
PointerReleasedEvent.AddClassHandler<InputElement>(x => x.OnPointerReleased);
PointerWheelChangedEvent.AddClassHandler<InputElement>(x => x.OnPointerWheelChanged);
GotFocusEvent.AddClassHandler<InputElement>(x => x.OnGotFocus, RoutingStrategies.Bubble);
LostFocusEvent.AddClassHandler<InputElement>(x => x.OnLostFocus, RoutingStrategies.Bubble);
KeyDownEvent.AddClassHandler<InputElement>(x => x.OnKeyDown, RoutingStrategies.Bubble);
PreviewKeyDownEvent.AddClassHandler<InputElement>(x => x.OnPreviewKeyDown, RoutingStrategies.Tunnel);
PointerEnterEvent.AddClassHandler<InputElement>(x => x.OnPointerEnter, RoutingStrategies.Direct);
PointerLeaveEvent.AddClassHandler<InputElement>(x => x.OnPointerLeave, RoutingStrategies.Direct);
PointerMovedEvent.AddClassHandler<InputElement>(x => x.OnPointerMoved, RoutingStrategies.Bubble);
PointerPressedEvent.AddClassHandler<InputElement>(x => x.OnPointerPressed, RoutingStrategies.Bubble);
PointerReleasedEvent.AddClassHandler<InputElement>(x => x.OnPointerReleased, RoutingStrategies.Bubble);
PointerWheelChangedEvent.AddClassHandler<InputElement>(x => x.OnPointerWheelChanged, RoutingStrategies.Bubble);
}
public event EventHandler<RoutedEventArgs> GotFocus

96
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<string>();
EventHandler<RoutedEventArgs> 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<string>();
EventHandler<RoutedEventArgs> 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<string>();
EventHandler<RoutedEventArgs> 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<RoutedEventArgs>(
"test",
RoutingStrategies.Bubble | RoutingStrategies.Tunnel,
typeof(TestInteractive));
var target = this.CreateTree(ev, null, 0);
ev.AddClassHandler<TestInteractive>(x => x.ClassHandler, RoutingStrategies.Bubble);
var args = new RoutedEventArgs(ev, target);
target.RaiseEvent(args);
Assert.True(target.ClassHandlerInvoked);
Assert.True(target.GetVisualParent<TestInteractive>().ClassHandlerInvoked);
}
private TestInteractive CreateTree(
RoutedEvent ev,
EventHandler<RoutedEventArgs> handler,
@ -247,9 +329,12 @@ namespace Perspex.Interactive.UnitTests
}
};
foreach (var i in tree.GetSelfAndVisualDescendents().Cast<Interactive>())
if (handler != null)
{
i.AddHandler(ev, handler, handlerRoutes, handledEventsToo);
foreach (var i in tree.GetSelfAndVisualDescendents().Cast<Interactive>())
{
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<IVisual> Children
{
get
@ -271,6 +358,11 @@ namespace Perspex.Interactive.UnitTests
this.AddVisualChildren(value.Cast<Visual>());
}
}
public void ClassHandler(RoutedEventArgs e)
{
this.ClassHandlerInvoked = true;
}
}
}
}

17
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; }
}
}

26
Perspex.Interactivity/Interactive.cs

@ -16,8 +16,8 @@ namespace Perspex.Interactivity
public class Interactive : Layoutable, IInteractive
{
private Dictionary<RoutedEvent, List<Subscription>> eventHandlers =
new Dictionary<RoutedEvent, List<Subscription>>();
private Dictionary<RoutedEvent, List<EventSubscription>> eventHandlers =
new Dictionary<RoutedEvent, List<EventSubscription>>();
public void AddHandler(
RoutedEvent routedEvent,
@ -28,15 +28,15 @@ namespace Perspex.Interactivity
Contract.Requires<NullReferenceException>(routedEvent != null);
Contract.Requires<NullReferenceException>(handler != null);
List<Subscription> subscriptions;
List<EventSubscription> subscriptions;
if (!this.eventHandlers.TryGetValue(routedEvent, out subscriptions))
{
subscriptions = new List<Subscription>();
subscriptions = new List<EventSubscription>();
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<NullReferenceException>(routedEvent != null);
Contract.Requires<NullReferenceException>(handler != null);
List<Subscription> subscriptions;
List<EventSubscription> 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<NullReferenceException>(e != null);
List<Subscription> subscriptions;
e.RoutedEvent.InvokeClassHandlers(this, e);
List<EventSubscription> 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; }
}
}
}

5
Perspex.Interactivity/Perspex.Interactivity.csproj

@ -8,8 +8,8 @@
<ProjectGuid>{6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Perspex</RootNamespace>
<AssemblyName>Perspex.Interactive</AssemblyName>
<RootNamespace>Perspex.Interactivity</RootNamespace>
<AssemblyName>Perspex.Interactivity</AssemblyName>
<DefaultLanguage>en-US</DefaultLanguage>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
@ -58,6 +58,7 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RoutedEvent.cs" />
<Compile Include="RoutedEventArgs.cs" />
<Compile Include="EventSubscription.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="Splat">

45
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<ClassEventSubscription> subscriptions = new List<ClassEventSubscription>();
public RoutedEvent(
string name,
RoutingStrategies routingStrategies,
@ -86,13 +89,33 @@ namespace Perspex.Interactivity
return new RoutedEvent<TEventArgs>(name, routingStrategy, ownerType);
}
internal void InvokeRaised(object sender, RoutedEventArgs e)
public void AddClassHandler(Type type, EventHandler<RoutedEventArgs> 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<TEventArgs> : RoutedEvent
@ -106,18 +129,12 @@ namespace Perspex.Interactivity
Contract.Requires<InvalidCastException>(typeof(IInteractive).GetTypeInfo().IsAssignableFrom(ownerType.GetTypeInfo()));
}
public void AddClassHandler<TTarget>(Func<TTarget, Action<TEventArgs>> handler) where TTarget : class
public void AddClassHandler<TTarget>(
Func<TTarget, Action<TEventArgs>> 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<RoutedEventArgs>)((s, e) => handler((TTarget)s)((TEventArgs)e));
this.AddClassHandler(typeof(TTarget), adaptor, routes);
}
}
}

Loading…
Cancel
Save