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() static Thumb()
{ {
DragStartedEvent.AddClassHandler<Thumb>(x => x.OnDragStarted); DragStartedEvent.AddClassHandler<Thumb>(x => x.OnDragStarted, RoutingStrategies.Bubble);
DragDeltaEvent.AddClassHandler<Thumb>(x => x.OnDragDelta); DragDeltaEvent.AddClassHandler<Thumb>(x => x.OnDragDelta, RoutingStrategies.Bubble);
DragCompletedEvent.AddClassHandler<Thumb>(x => x.OnDragCompleted); DragCompletedEvent.AddClassHandler<Thumb>(x => x.OnDragCompleted, RoutingStrategies.Bubble);
} }
public event EventHandler<VectorEventArgs> DragStarted public event EventHandler<VectorEventArgs> DragStarted

20
Perspex.Input/InputElement.cs

@ -66,16 +66,16 @@ namespace Perspex.Input
{ {
IsEnabledProperty.Changed.Subscribe(IsEnabledChanged); IsEnabledProperty.Changed.Subscribe(IsEnabledChanged);
GotFocusEvent.AddClassHandler<InputElement>(x => x.OnGotFocus); GotFocusEvent.AddClassHandler<InputElement>(x => x.OnGotFocus, RoutingStrategies.Bubble);
LostFocusEvent.AddClassHandler<InputElement>(x => x.OnLostFocus); LostFocusEvent.AddClassHandler<InputElement>(x => x.OnLostFocus, RoutingStrategies.Bubble);
KeyDownEvent.AddClassHandler<InputElement>(x => x.OnKeyDown); KeyDownEvent.AddClassHandler<InputElement>(x => x.OnKeyDown, RoutingStrategies.Bubble);
PreviewKeyDownEvent.AddClassHandler<InputElement>(x => x.OnPreviewKeyDown); PreviewKeyDownEvent.AddClassHandler<InputElement>(x => x.OnPreviewKeyDown, RoutingStrategies.Tunnel);
PointerEnterEvent.AddClassHandler<InputElement>(x => x.OnPointerEnter); PointerEnterEvent.AddClassHandler<InputElement>(x => x.OnPointerEnter, RoutingStrategies.Direct);
PointerLeaveEvent.AddClassHandler<InputElement>(x => x.OnPointerLeave); PointerLeaveEvent.AddClassHandler<InputElement>(x => x.OnPointerLeave, RoutingStrategies.Direct);
PointerMovedEvent.AddClassHandler<InputElement>(x => x.OnPointerMoved); PointerMovedEvent.AddClassHandler<InputElement>(x => x.OnPointerMoved, RoutingStrategies.Bubble);
PointerPressedEvent.AddClassHandler<InputElement>(x => x.OnPointerPressed); PointerPressedEvent.AddClassHandler<InputElement>(x => x.OnPointerPressed, RoutingStrategies.Bubble);
PointerReleasedEvent.AddClassHandler<InputElement>(x => x.OnPointerReleased); PointerReleasedEvent.AddClassHandler<InputElement>(x => x.OnPointerReleased, RoutingStrategies.Bubble);
PointerWheelChangedEvent.AddClassHandler<InputElement>(x => x.OnPointerWheelChanged); PointerWheelChangedEvent.AddClassHandler<InputElement>(x => x.OnPointerWheelChanged, RoutingStrategies.Bubble);
} }
public event EventHandler<RoutedEventArgs> GotFocus 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); 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( private TestInteractive CreateTree(
RoutedEvent ev, RoutedEvent ev,
EventHandler<RoutedEventArgs> handler, 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; return target;
@ -259,6 +344,8 @@ namespace Perspex.Interactive.UnitTests
{ {
public string Id { get; set; } public string Id { get; set; }
public bool ClassHandlerInvoked { get; private set; }
public IEnumerable<IVisual> Children public IEnumerable<IVisual> Children
{ {
get get
@ -271,6 +358,11 @@ namespace Perspex.Interactive.UnitTests
this.AddVisualChildren(value.Cast<Visual>()); 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 public class Interactive : Layoutable, IInteractive
{ {
private Dictionary<RoutedEvent, List<Subscription>> eventHandlers = private Dictionary<RoutedEvent, List<EventSubscription>> eventHandlers =
new Dictionary<RoutedEvent, List<Subscription>>(); new Dictionary<RoutedEvent, List<EventSubscription>>();
public void AddHandler( public void AddHandler(
RoutedEvent routedEvent, RoutedEvent routedEvent,
@ -28,15 +28,15 @@ namespace Perspex.Interactivity
Contract.Requires<NullReferenceException>(routedEvent != null); Contract.Requires<NullReferenceException>(routedEvent != null);
Contract.Requires<NullReferenceException>(handler != null); Contract.Requires<NullReferenceException>(handler != null);
List<Subscription> subscriptions; List<EventSubscription> subscriptions;
if (!this.eventHandlers.TryGetValue(routedEvent, out subscriptions)) if (!this.eventHandlers.TryGetValue(routedEvent, out subscriptions))
{ {
subscriptions = new List<Subscription>(); subscriptions = new List<EventSubscription>();
this.eventHandlers.Add(routedEvent, subscriptions); this.eventHandlers.Add(routedEvent, subscriptions);
} }
subscriptions.Add(new Subscription subscriptions.Add(new EventSubscription
{ {
Handler = handler, Handler = handler,
Routes = routes, Routes = routes,
@ -58,7 +58,7 @@ namespace Perspex.Interactivity
Contract.Requires<NullReferenceException>(routedEvent != null); Contract.Requires<NullReferenceException>(routedEvent != null);
Contract.Requires<NullReferenceException>(handler != null); Contract.Requires<NullReferenceException>(handler != null);
List<Subscription> subscriptions; List<EventSubscription> subscriptions;
if (this.eventHandlers.TryGetValue(routedEvent, out subscriptions)) if (this.eventHandlers.TryGetValue(routedEvent, out subscriptions))
{ {
@ -76,6 +76,7 @@ namespace Perspex.Interactivity
if (e.RoutedEvent.RoutingStrategies == RoutingStrategies.Direct) if (e.RoutedEvent.RoutingStrategies == RoutingStrategies.Direct)
{ {
e.Route = RoutingStrategies.Direct; e.Route = RoutingStrategies.Direct;
e.RoutedEvent.InvokeClassHandlers(this, e);
this.RaiseEventImpl(e); this.RaiseEventImpl(e);
} }
@ -118,7 +119,9 @@ namespace Perspex.Interactivity
{ {
Contract.Requires<NullReferenceException>(e != null); Contract.Requires<NullReferenceException>(e != null);
List<Subscription> subscriptions; e.RoutedEvent.InvokeClassHandlers(this, e);
List<EventSubscription> subscriptions;
if (this.eventHandlers.TryGetValue(e.RoutedEvent, out 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> <ProjectGuid>{6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}</ProjectGuid>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder> <AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Perspex</RootNamespace> <RootNamespace>Perspex.Interactivity</RootNamespace>
<AssemblyName>Perspex.Interactive</AssemblyName> <AssemblyName>Perspex.Interactivity</AssemblyName>
<DefaultLanguage>en-US</DefaultLanguage> <DefaultLanguage>en-US</DefaultLanguage>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
@ -58,6 +58,7 @@
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RoutedEvent.cs" /> <Compile Include="RoutedEvent.cs" />
<Compile Include="RoutedEventArgs.cs" /> <Compile Include="RoutedEventArgs.cs" />
<Compile Include="EventSubscription.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Splat"> <Reference Include="Splat">

45
Perspex.Interactivity/RoutedEvent.cs

@ -7,6 +7,7 @@
namespace Perspex.Interactivity namespace Perspex.Interactivity
{ {
using System; using System;
using System.Collections.Generic;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Reflection; using System.Reflection;
@ -20,6 +21,8 @@ namespace Perspex.Interactivity
public class RoutedEvent public class RoutedEvent
{ {
private List<ClassEventSubscription> subscriptions = new List<ClassEventSubscription>();
public RoutedEvent( public RoutedEvent(
string name, string name,
RoutingStrategies routingStrategies, RoutingStrategies routingStrategies,
@ -86,13 +89,33 @@ namespace Perspex.Interactivity
return new RoutedEvent<TEventArgs>(name, routingStrategy, ownerType); 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 public class RoutedEvent<TEventArgs> : RoutedEvent
@ -106,18 +129,12 @@ namespace Perspex.Interactivity
Contract.Requires<InvalidCastException>(typeof(IInteractive).GetTypeInfo().IsAssignableFrom(ownerType.GetTypeInfo())); 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 adaptor = (EventHandler<RoutedEventArgs>)((s, e) => handler((TTarget)s)((TEventArgs)e));
{ this.AddClassHandler(typeof(TTarget), adaptor, routes);
var target = s as TTarget;
var args = e as TEventArgs;
if (target != null)
{
handler(target)(args);
}
};
} }
} }
} }

Loading…
Cancel
Save