diff --git a/src/Avalonia.Controls/Calendar/DatePicker.cs b/src/Avalonia.Controls/Calendar/DatePicker.cs
index ea06bdb394..07e42c64e4 100644
--- a/src/Avalonia.Controls/Calendar/DatePicker.cs
+++ b/src/Avalonia.Controls/Calendar/DatePicker.cs
@@ -476,7 +476,7 @@ namespace Avalonia.Controls
{
_dropDownButton.Click += DropDownButton_Click;
_buttonPointerPressedSubscription =
- _dropDownButton.AddHandler(PointerPressedEvent, DropDownButton_PointerPressed, handledEventsToo: true);
+ _dropDownButton.AddDisposableHandler(PointerPressedEvent, DropDownButton_PointerPressed, handledEventsToo: true);
}
if (_textBox != null)
diff --git a/src/Avalonia.Controls/ComboBox.cs b/src/Avalonia.Controls/ComboBox.cs
index 9d471a0fc0..d1f54da23a 100644
--- a/src/Avalonia.Controls/ComboBox.cs
+++ b/src/Avalonia.Controls/ComboBox.cs
@@ -9,6 +9,7 @@ using Avalonia.Controls.Primitives;
using Avalonia.Controls.Shapes;
using Avalonia.Controls.Templates;
using Avalonia.Input;
+using Avalonia.Interactivity;
using Avalonia.LogicalTree;
using Avalonia.Media;
using Avalonia.VisualTree;
@@ -265,7 +266,7 @@ namespace Avalonia.Controls
var toplevel = this.GetVisualRoot() as TopLevel;
if (toplevel != null)
{
- _subscriptionsOnOpen = toplevel.AddHandler(PointerWheelChangedEvent, (s, ev) =>
+ _subscriptionsOnOpen = toplevel.AddDisposableHandler(PointerWheelChangedEvent, (s, ev) =>
{
//eat wheel scroll event outside dropdown popup while it's open
if (IsDropDownOpen && (ev.Source as IVisual).GetVisualRoot() == toplevel)
diff --git a/src/Avalonia.Controls/Primitives/Popup.cs b/src/Avalonia.Controls/Primitives/Popup.cs
index 9dd481cf40..1a12b53b9d 100644
--- a/src/Avalonia.Controls/Primitives/Popup.cs
+++ b/src/Avalonia.Controls/Primitives/Popup.cs
@@ -279,7 +279,7 @@ namespace Avalonia.Controls.Primitives
}
}
- DeferCleanup(topLevel.AddHandler(PointerPressedEvent, PointerPressedOutside, RoutingStrategies.Tunnel));
+ DeferCleanup(topLevel.AddDisposableHandler(PointerPressedEvent, PointerPressedOutside, RoutingStrategies.Tunnel));
DeferCleanup(InputManager.Instance?.Process.Subscribe(ListenForNonClientClick));
diff --git a/src/Avalonia.Diagnostics/Diagnostics/DevTools.cs b/src/Avalonia.Diagnostics/Diagnostics/DevTools.cs
index 0464047273..b3a8f4745e 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/DevTools.cs
+++ b/src/Avalonia.Diagnostics/Diagnostics/DevTools.cs
@@ -22,7 +22,7 @@ namespace Avalonia.Diagnostics
}
}
- return root.AddHandler(
+ return root.AddDisposableHandler(
InputElement.KeyDownEvent,
PreviewKeyDown,
RoutingStrategies.Tunnel);
diff --git a/src/Avalonia.Interactivity/IInteractive.cs b/src/Avalonia.Interactivity/IInteractive.cs
index 33baa9453a..51aee78988 100644
--- a/src/Avalonia.Interactivity/IInteractive.cs
+++ b/src/Avalonia.Interactivity/IInteractive.cs
@@ -23,7 +23,7 @@ namespace Avalonia.Interactivity
/// The routing strategies to listen to.
/// Whether handled events should also be listened for.
/// A disposable that terminates the event subscription.
- IDisposable AddHandler(
+ void AddHandler(
RoutedEvent routedEvent,
Delegate handler,
RoutingStrategies routes = RoutingStrategies.Direct | RoutingStrategies.Bubble,
@@ -38,7 +38,7 @@ namespace Avalonia.Interactivity
/// The routing strategies to listen to.
/// Whether handled events should also be listened for.
/// A disposable that terminates the event subscription.
- IDisposable AddHandler(
+ void AddHandler(
RoutedEvent routedEvent,
EventHandler handler,
RoutingStrategies routes = RoutingStrategies.Direct | RoutingStrategies.Bubble,
diff --git a/src/Avalonia.Interactivity/Interactive.cs b/src/Avalonia.Interactivity/Interactive.cs
index 9493d86885..321ecbc516 100644
--- a/src/Avalonia.Interactivity/Interactive.cs
+++ b/src/Avalonia.Interactivity/Interactive.cs
@@ -27,8 +27,7 @@ namespace Avalonia.Interactivity
/// The handler.
/// The routing strategies to listen to.
/// Whether handled events should also be listened for.
- /// A disposable that terminates the event subscription.
- public IDisposable AddHandler(
+ public void AddHandler(
RoutedEvent routedEvent,
Delegate handler,
RoutingStrategies routes = RoutingStrategies.Direct | RoutingStrategies.Bubble,
@@ -38,7 +37,8 @@ namespace Avalonia.Interactivity
handler = handler ?? throw new ArgumentNullException(nameof(handler));
var subscription = new EventSubscription(handler, routes, handledEventsToo);
- return AddEventSubscription(routedEvent, subscription);
+
+ AddEventSubscription(routedEvent, subscription);
}
///
@@ -49,8 +49,7 @@ namespace Avalonia.Interactivity
/// The handler.
/// The routing strategies to listen to.
/// Whether handled events should also be listened for.
- /// A disposable that terminates the event subscription.
- public IDisposable AddHandler(
+ public void AddHandler(
RoutedEvent routedEvent,
EventHandler handler,
RoutingStrategies routes = RoutingStrategies.Direct | RoutingStrategies.Bubble,
@@ -69,7 +68,7 @@ namespace Avalonia.Interactivity
var subscription = new EventSubscription(handler, routes, handledEventsToo, (baseHandler, sender, args) => InvokeAdapter(baseHandler, sender, args));
- return AddEventSubscription(routedEvent, subscription);
+ AddEventSubscription(routedEvent, subscription);
}
///
@@ -188,7 +187,7 @@ namespace Avalonia.Interactivity
return result;
}
- private IDisposable AddEventSubscription(RoutedEvent routedEvent, EventSubscription subscription)
+ private void AddEventSubscription(RoutedEvent routedEvent, EventSubscription subscription)
{
_eventHandlers ??= new Dictionary>();
@@ -199,8 +198,6 @@ namespace Avalonia.Interactivity
}
subscriptions.Add(subscription);
-
- return new UnsubscribeDisposable(subscriptions, subscription);
}
private readonly struct EventSubscription
@@ -225,22 +222,5 @@ namespace Avalonia.Interactivity
public bool HandledEventsToo { get; }
}
-
- private sealed class UnsubscribeDisposable : IDisposable
- {
- private readonly List _subscriptions;
- private readonly EventSubscription _subscription;
-
- public UnsubscribeDisposable(List subscriptions, EventSubscription subscription)
- {
- _subscriptions = subscriptions;
- _subscription = subscription;
- }
-
- public void Dispose()
- {
- _subscriptions.Remove(_subscription);
- }
- }
}
}
diff --git a/src/Avalonia.Interactivity/InteractiveExtensions.cs b/src/Avalonia.Interactivity/InteractiveExtensions.cs
index 414c408080..e6c93e26b2 100644
--- a/src/Avalonia.Interactivity/InteractiveExtensions.cs
+++ b/src/Avalonia.Interactivity/InteractiveExtensions.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
+using System.Reactive.Disposables;
using System.Reactive.Linq;
namespace Avalonia.Interactivity
@@ -11,6 +12,28 @@ namespace Avalonia.Interactivity
///
public static class InteractiveExtensions
{
+ ///
+ /// Adds a handler for the specified routed event and returns a disposable that can terminate the event subscription.
+ ///
+ /// The type of the event's args.
+ /// Target for adding given event handler.
+ /// The routed event.
+ /// The handler.
+ /// The routing strategies to listen to.
+ /// Whether handled events should also be listened for.
+ /// A disposable that terminates the event subscription.
+ public static IDisposable AddDisposableHandler(this IInteractive o, RoutedEvent routedEvent,
+ EventHandler handler,
+ RoutingStrategies routes = RoutingStrategies.Direct | RoutingStrategies.Bubble,
+ bool handledEventsToo = false) where TEventArgs : RoutedEventArgs
+ {
+ o.AddHandler(routedEvent, handler, routes, handledEventsToo);
+
+ return Disposable.Create(
+ (instance: o, handler, routedEvent),
+ state => state.instance.RemoveHandler(state.routedEvent, state.handler));
+ }
+
///
/// Gets an observable for a .
///
@@ -31,7 +54,7 @@ namespace Avalonia.Interactivity
o = o ?? throw new ArgumentNullException(nameof(o));
routedEvent = routedEvent ?? throw new ArgumentNullException(nameof(routedEvent));
- return Observable.Create(x => o.AddHandler(
+ return Observable.Create(x => o.AddDisposableHandler(
routedEvent,
(_, e) => x.OnNext(e),
routes,