@ -4,8 +4,6 @@
using System ;
using System.Collections.Generic ;
using System.Linq ;
using System.Reactive.Disposables ;
using System.Reactive.Linq ;
using Avalonia.Layout ;
using Avalonia.VisualTree ;
@ -18,15 +16,14 @@ namespace Avalonia.Interactivity
{
private Dictionary < RoutedEvent , List < EventSubscription > > _ eventHandlers ;
private static readonly Dictionary < Type , HandlerInvokeSignature > s_invokeHandlerCache = new Dictionary < Type , HandlerInvokeSignature > ( ) ;
/// <summary>
/// Gets the interactive parent of the object for bubbling and tunneling events.
/// </summary>
IInteractive IInteractive . InteractiveParent = > ( ( IVisual ) this ) . VisualParent as IInteractive ;
private Dictionary < RoutedEvent , List < EventSubscription > > EventHandlers
{
get { return _ eventHandlers ? ? ( _ eventHandlers = new Dictionary < RoutedEvent , List < EventSubscription > > ( ) ) ; }
}
private Dictionary < RoutedEvent , List < EventSubscription > > EventHandlers = > _ eventHandlers ? ? ( _ eventHandlers = new Dictionary < RoutedEvent , List < EventSubscription > > ( ) ) ;
/// <summary>
/// Adds a handler for the specified routed event.
@ -45,24 +42,14 @@ namespace Avalonia.Interactivity
Contract . Requires < ArgumentNullException > ( routedEvent ! = null ) ;
Contract . Requires < ArgumentNullException > ( handler ! = null ) ;
List < EventSubscription > subscriptions ;
if ( ! EventHandlers . TryGetValue ( routedEvent , out subscriptions ) )
{
subscriptions = new List < EventSubscription > ( ) ;
EventHandlers . Add ( routedEvent , subscriptions ) ;
}
var sub = new EventSubscription
var subscription = new EventSubscription
{
Handler = handler ,
Routes = routes ,
AlsoIfHandled = handledEventsToo ,
} ;
subscriptions . Add ( sub ) ;
return Disposable . Create ( ( ) = > subscriptions . Remove ( sub ) ) ;
return AddEventSubscription ( routedEvent , subscription ) ;
}
/// <summary>
@ -80,7 +67,37 @@ namespace Avalonia.Interactivity
RoutingStrategies routes = RoutingStrategies . Direct | RoutingStrategies . Bubble ,
bool handledEventsToo = false ) where TEventArgs : RoutedEventArgs
{
return AddHandler ( routedEvent , ( Delegate ) handler , routes , handledEventsToo ) ;
Contract . Requires < ArgumentNullException > ( routedEvent ! = null ) ;
Contract . Requires < ArgumentNullException > ( handler ! = null ) ;
// EventHandler delegate is not covariant, this forces us to create small wrapper
// that will cast our type erased instance and invoke it.
Type eventArgsType = routedEvent . EventArgsType ;
if ( ! s_invokeHandlerCache . TryGetValue ( eventArgsType , out var invokeAdapter ) )
{
void InvokeAdapter ( Delegate baseHandler , object sender , RoutedEventArgs args )
{
var typedHandler = ( EventHandler < TEventArgs > ) baseHandler ;
var typedArgs = ( TEventArgs ) args ;
typedHandler ( sender , typedArgs ) ;
}
invokeAdapter = InvokeAdapter ;
s_invokeHandlerCache . Add ( eventArgsType , invokeAdapter ) ;
}
var subscription = new EventSubscription
{
InvokeAdapter = invokeAdapter ,
Handler = handler ,
Routes = routes ,
AlsoIfHandled = handledEventsToo ,
} ;
return AddEventSubscription ( routedEvent , subscription ) ;
}
/// <summary>
@ -196,10 +213,54 @@ namespace Avalonia.Interactivity
if ( correctRoute & & notFinished )
{
sub . Handler . DynamicInvoke ( this , e ) ;
if ( sub . InvokeAdapter ! = null )
{
sub . InvokeAdapter ( sub . Handler , this , e ) ;
}
else
{
sub . Handler . DynamicInvoke ( this , e ) ;
}
}
}
}
}
private List < EventSubscription > GetEventSubscriptions ( RoutedEvent routedEvent )
{
if ( ! EventHandlers . TryGetValue ( routedEvent , out var subscriptions ) )
{
subscriptions = new List < EventSubscription > ( ) ;
EventHandlers . Add ( routedEvent , subscriptions ) ;
}
return subscriptions ;
}
private IDisposable AddEventSubscription ( RoutedEvent routedEvent , EventSubscription subscription )
{
List < EventSubscription > subscriptions = GetEventSubscriptions ( routedEvent ) ;
subscriptions . Add ( subscription ) ;
return new UnsubscribeDisposable ( subscriptions , subscription ) ;
}
private sealed class UnsubscribeDisposable : IDisposable
{
private readonly List < EventSubscription > _ subscriptions ;
private readonly EventSubscription _ subscription ;
public UnsubscribeDisposable ( List < EventSubscription > subscriptions , EventSubscription subscription )
{
_ subscriptions = subscriptions ;
_ subscription = subscription ;
}
public void Dispose ( )
{
_ subscriptions . Remove ( _ subscription ) ;
}
}
}
}