浏览代码

Avoid creating handler unsubscribe disposables unless explicitly requested.

Dariusz Komosinski 5 年之前
父节点
当前提交
35bce7b031

+ 1 - 1
src/Avalonia.Controls/Calendar/DatePicker.cs

@@ -476,7 +476,7 @@ namespace Avalonia.Controls
             {
             {
                 _dropDownButton.Click += DropDownButton_Click;
                 _dropDownButton.Click += DropDownButton_Click;
                 _buttonPointerPressedSubscription =
                 _buttonPointerPressedSubscription =
-                    _dropDownButton.AddHandler(PointerPressedEvent, DropDownButton_PointerPressed, handledEventsToo: true);
+                    _dropDownButton.AddDisposableHandler(PointerPressedEvent, DropDownButton_PointerPressed, handledEventsToo: true);
             }
             }
 
 
             if (_textBox != null)
             if (_textBox != null)

+ 2 - 1
src/Avalonia.Controls/ComboBox.cs

@@ -9,6 +9,7 @@ using Avalonia.Controls.Primitives;
 using Avalonia.Controls.Shapes;
 using Avalonia.Controls.Shapes;
 using Avalonia.Controls.Templates;
 using Avalonia.Controls.Templates;
 using Avalonia.Input;
 using Avalonia.Input;
+using Avalonia.Interactivity;
 using Avalonia.LogicalTree;
 using Avalonia.LogicalTree;
 using Avalonia.Media;
 using Avalonia.Media;
 using Avalonia.VisualTree;
 using Avalonia.VisualTree;
@@ -265,7 +266,7 @@ namespace Avalonia.Controls
             var toplevel = this.GetVisualRoot() as TopLevel;
             var toplevel = this.GetVisualRoot() as TopLevel;
             if (toplevel != null)
             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
                     //eat wheel scroll event outside dropdown popup while it's open
                     if (IsDropDownOpen && (ev.Source as IVisual).GetVisualRoot() == toplevel)
                     if (IsDropDownOpen && (ev.Source as IVisual).GetVisualRoot() == toplevel)

+ 1 - 1
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));
             DeferCleanup(InputManager.Instance?.Process.Subscribe(ListenForNonClientClick));
 
 

+ 1 - 1
src/Avalonia.Diagnostics/Diagnostics/DevTools.cs

@@ -22,7 +22,7 @@ namespace Avalonia.Diagnostics
                 }
                 }
             }
             }
 
 
-            return root.AddHandler(
+            return root.AddDisposableHandler(
                 InputElement.KeyDownEvent,
                 InputElement.KeyDownEvent,
                 PreviewKeyDown,
                 PreviewKeyDown,
                 RoutingStrategies.Tunnel);
                 RoutingStrategies.Tunnel);

+ 2 - 2
src/Avalonia.Interactivity/IInteractive.cs

@@ -23,7 +23,7 @@ namespace Avalonia.Interactivity
         /// <param name="routes">The routing strategies to listen to.</param>
         /// <param name="routes">The routing strategies to listen to.</param>
         /// <param name="handledEventsToo">Whether handled events should also be listened for.</param>
         /// <param name="handledEventsToo">Whether handled events should also be listened for.</param>
         /// <returns>A disposable that terminates the event subscription.</returns>
         /// <returns>A disposable that terminates the event subscription.</returns>
-        IDisposable AddHandler(
+        void AddHandler(
             RoutedEvent routedEvent,
             RoutedEvent routedEvent,
             Delegate handler,
             Delegate handler,
             RoutingStrategies routes = RoutingStrategies.Direct | RoutingStrategies.Bubble,
             RoutingStrategies routes = RoutingStrategies.Direct | RoutingStrategies.Bubble,
@@ -38,7 +38,7 @@ namespace Avalonia.Interactivity
         /// <param name="routes">The routing strategies to listen to.</param>
         /// <param name="routes">The routing strategies to listen to.</param>
         /// <param name="handledEventsToo">Whether handled events should also be listened for.</param>
         /// <param name="handledEventsToo">Whether handled events should also be listened for.</param>
         /// <returns>A disposable that terminates the event subscription.</returns>
         /// <returns>A disposable that terminates the event subscription.</returns>
-        IDisposable AddHandler<TEventArgs>(
+        void AddHandler<TEventArgs>(
             RoutedEvent<TEventArgs> routedEvent,
             RoutedEvent<TEventArgs> routedEvent,
             EventHandler<TEventArgs> handler,
             EventHandler<TEventArgs> handler,
             RoutingStrategies routes = RoutingStrategies.Direct | RoutingStrategies.Bubble,
             RoutingStrategies routes = RoutingStrategies.Direct | RoutingStrategies.Bubble,

+ 6 - 26
src/Avalonia.Interactivity/Interactive.cs

@@ -27,8 +27,7 @@ namespace Avalonia.Interactivity
         /// <param name="handler">The handler.</param>
         /// <param name="handler">The handler.</param>
         /// <param name="routes">The routing strategies to listen to.</param>
         /// <param name="routes">The routing strategies to listen to.</param>
         /// <param name="handledEventsToo">Whether handled events should also be listened for.</param>
         /// <param name="handledEventsToo">Whether handled events should also be listened for.</param>
-        /// <returns>A disposable that terminates the event subscription.</returns>
-        public IDisposable AddHandler(
+        public void AddHandler(
             RoutedEvent routedEvent,
             RoutedEvent routedEvent,
             Delegate handler,
             Delegate handler,
             RoutingStrategies routes = RoutingStrategies.Direct | RoutingStrategies.Bubble,
             RoutingStrategies routes = RoutingStrategies.Direct | RoutingStrategies.Bubble,
@@ -38,7 +37,8 @@ namespace Avalonia.Interactivity
             handler = handler ?? throw new ArgumentNullException(nameof(handler));
             handler = handler ?? throw new ArgumentNullException(nameof(handler));
 
 
             var subscription = new EventSubscription(handler, routes, handledEventsToo);
             var subscription = new EventSubscription(handler, routes, handledEventsToo);
-            return AddEventSubscription(routedEvent, subscription);
+
+            AddEventSubscription(routedEvent, subscription);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -49,8 +49,7 @@ namespace Avalonia.Interactivity
         /// <param name="handler">The handler.</param>
         /// <param name="handler">The handler.</param>
         /// <param name="routes">The routing strategies to listen to.</param>
         /// <param name="routes">The routing strategies to listen to.</param>
         /// <param name="handledEventsToo">Whether handled events should also be listened for.</param>
         /// <param name="handledEventsToo">Whether handled events should also be listened for.</param>
-        /// <returns>A disposable that terminates the event subscription.</returns>
-        public IDisposable AddHandler<TEventArgs>(
+        public void AddHandler<TEventArgs>(
             RoutedEvent<TEventArgs> routedEvent,
             RoutedEvent<TEventArgs> routedEvent,
             EventHandler<TEventArgs> handler,
             EventHandler<TEventArgs> handler,
             RoutingStrategies routes = RoutingStrategies.Direct | RoutingStrategies.Bubble,
             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));
             var subscription = new EventSubscription(handler, routes, handledEventsToo, (baseHandler, sender, args) => InvokeAdapter(baseHandler, sender, args));
 
 
-            return AddEventSubscription(routedEvent, subscription);
+            AddEventSubscription(routedEvent, subscription);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -188,7 +187,7 @@ namespace Avalonia.Interactivity
             return result;
             return result;
         }
         }
 
 
-        private IDisposable AddEventSubscription(RoutedEvent routedEvent, EventSubscription subscription)
+        private void AddEventSubscription(RoutedEvent routedEvent, EventSubscription subscription)
         {
         {
             _eventHandlers ??= new Dictionary<RoutedEvent, List<EventSubscription>>();
             _eventHandlers ??= new Dictionary<RoutedEvent, List<EventSubscription>>();
 
 
@@ -199,8 +198,6 @@ namespace Avalonia.Interactivity
             }
             }
 
 
             subscriptions.Add(subscription);
             subscriptions.Add(subscription);
-
-            return new UnsubscribeDisposable(subscriptions, subscription);
         }
         }
 
 
         private readonly struct EventSubscription
         private readonly struct EventSubscription
@@ -225,22 +222,5 @@ namespace Avalonia.Interactivity
 
 
             public bool HandledEventsToo { get; }
             public bool HandledEventsToo { get; }
         }
         }
-
-        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);
-            }
-        }
     }
     }
 }
 }

+ 24 - 1
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.
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
 
 using System;
 using System;
+using System.Reactive.Disposables;
 using System.Reactive.Linq;
 using System.Reactive.Linq;
 
 
 namespace Avalonia.Interactivity
 namespace Avalonia.Interactivity
@@ -11,6 +12,28 @@ namespace Avalonia.Interactivity
     /// </summary>
     /// </summary>
     public static class InteractiveExtensions
     public static class InteractiveExtensions
     {
     {
+        /// <summary>
+        /// Adds a handler for the specified routed event and returns a disposable that can terminate the event subscription.
+        /// </summary>
+        /// <typeparam name="TEventArgs">The type of the event's args.</typeparam>
+        /// <param name="o">Target for adding given event handler.</param>
+        /// <param name="routedEvent">The routed event.</param>
+        /// <param name="handler">The handler.</param>
+        /// <param name="routes">The routing strategies to listen to.</param>
+        /// <param name="handledEventsToo">Whether handled events should also be listened for.</param>
+        /// <returns>A disposable that terminates the event subscription.</returns>
+        public static IDisposable AddDisposableHandler<TEventArgs>(this IInteractive o, RoutedEvent<TEventArgs> routedEvent,
+            EventHandler<TEventArgs> 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));
+        }
+
         /// <summary>
         /// <summary>
         /// Gets an observable for a <see cref="RoutedEvent{TEventArgs}"/>.
         /// Gets an observable for a <see cref="RoutedEvent{TEventArgs}"/>.
         /// </summary>
         /// </summary>
@@ -31,7 +54,7 @@ namespace Avalonia.Interactivity
             o = o ?? throw new ArgumentNullException(nameof(o));
             o = o ?? throw new ArgumentNullException(nameof(o));
             routedEvent = routedEvent ?? throw new ArgumentNullException(nameof(routedEvent));
             routedEvent = routedEvent ?? throw new ArgumentNullException(nameof(routedEvent));
 
 
-            return Observable.Create<TEventArgs>(x => o.AddHandler(
+            return Observable.Create<TEventArgs>(x => o.AddDisposableHandler(
                 routedEvent, 
                 routedEvent, 
                 (_, e) => x.OnNext(e), 
                 (_, e) => x.OnNext(e), 
                 routes,
                 routes,