From ce8530c41ee703208a3eae1a33c82459df6e4ceb Mon Sep 17 00:00:00 2001 From: chinboy Date: Sat, 1 Mar 2025 15:52:47 +0800 Subject: [PATCH] Fix ComboBox memory leak --- src/AtomUI.Controls/ComboBox/ComboBox.cs | 22 +++++++++- .../ComboBox/ComboBoxSpinnerInnerBox.cs | 24 +++++----- src/AtomUI.Controls/ComboBox/ComboBoxTheme.cs | 44 ++++++++++--------- 3 files changed, 57 insertions(+), 33 deletions(-) diff --git a/src/AtomUI.Controls/ComboBox/ComboBox.cs b/src/AtomUI.Controls/ComboBox/ComboBox.cs index f53e0bd86..029e35be7 100644 --- a/src/AtomUI.Controls/ComboBox/ComboBox.cs +++ b/src/AtomUI.Controls/ComboBox/ComboBox.cs @@ -1,10 +1,12 @@ -using AtomUI.Data; +using System.Reactive.Disposables; +using AtomUI.Data; using AtomUI.Theme; using AtomUI.Theme.Utils; using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Primitives; using Avalonia.Layout; +using Avalonia.LogicalTree; namespace AtomUI.Controls; @@ -12,7 +14,8 @@ using AvaloniaComboBox = Avalonia.Controls.ComboBox; public class ComboBox : AvaloniaComboBox, IAnimationAwareControl, - IControlSharedTokenResourcesHost + IControlSharedTokenResourcesHost, + ITokenResourceConsumer { public const string ErrorPC = ":error"; public const string WarningPC = ":warning"; @@ -116,8 +119,11 @@ public class ComboBox : AvaloniaComboBox, Control IAnimationAwareControl.PropertyBindTarget => this; Control IControlSharedTokenResourcesHost.HostControl => this; string IControlSharedTokenResourcesHost.TokenId => ComboBoxToken.ID; + CompositeDisposable? ITokenResourceConsumer.TokenBindingsDisposable => _tokenBindingsDisposable; #endregion + + private CompositeDisposable? _tokenBindingsDisposable; static ComboBox() { @@ -187,4 +193,16 @@ public class ComboBox : AvaloniaComboBox, PseudoClasses.Set(ErrorPC, Status == AddOnDecoratedStatus.Error); PseudoClasses.Set(WarningPC, Status == AddOnDecoratedStatus.Warning); } + + protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e) + { + base.OnAttachedToLogicalTree(e); + _tokenBindingsDisposable = new CompositeDisposable(); + } + + protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e) + { + base.OnDetachedFromLogicalTree(e); + this.DisposeTokenBindings(); + } } \ No newline at end of file diff --git a/src/AtomUI.Controls/ComboBox/ComboBoxSpinnerInnerBox.cs b/src/AtomUI.Controls/ComboBox/ComboBoxSpinnerInnerBox.cs index 33050f746..3b691c245 100644 --- a/src/AtomUI.Controls/ComboBox/ComboBoxSpinnerInnerBox.cs +++ b/src/AtomUI.Controls/ComboBox/ComboBoxSpinnerInnerBox.cs @@ -1,17 +1,19 @@ -using AtomUI.Theme.Data; +using AtomUI.Theme; +using AtomUI.Theme.Data; using AtomUI.Theme.Styling; using Avalonia; using Avalonia.Controls.Metadata; using Avalonia.Controls.Presenters; -using Avalonia.Controls.Primitives; using Avalonia.Data; +using Avalonia.LogicalTree; using Avalonia.Media; using Avalonia.Rendering; namespace AtomUI.Controls; [TemplatePart(ComboBoxSpinnerInnerBoxTheme.SpinnerHandlePart, typeof(ContentPresenter))] -internal class ComboBoxSpinnerInnerBox : AddOnDecoratedInnerBox, ICustomHitTest +internal class ComboBoxSpinnerInnerBox : AddOnDecoratedInnerBox, + ICustomHitTest { #region 公共属性定义 @@ -81,15 +83,15 @@ internal class ComboBoxSpinnerInnerBox : AddOnDecoratedInnerBox, ICustomHitTest return true; } - protected override void OnApplyTemplate(TemplateAppliedEventArgs e) + protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e) { - TokenResourceBinder.CreateTokenBinding(this, SpinnerBorderThicknessProperty, + base.OnAttachedToLogicalTree(e); + this.AddTokenBindingDisposable(TokenResourceBinder.CreateTokenBinding(this, SpinnerBorderThicknessProperty, SharedTokenKey.BorderThickness, BindingPriority.Template, - new RenderScaleAwareThicknessConfigure(this)); - TokenResourceBinder.CreateTokenBinding(this, SpinnerBorderBrushProperty, - SharedTokenKey.ColorBorder); - TokenResourceBinder.CreateTokenBinding(this, SpinnerHandleWidthTokenProperty, - ButtonSpinnerTokenKey.HandleWidth); - base.OnApplyTemplate(e); + new RenderScaleAwareThicknessConfigure(this))); + this.AddTokenBindingDisposable(TokenResourceBinder.CreateTokenBinding(this, SpinnerBorderBrushProperty, + SharedTokenKey.ColorBorder)); + this.AddTokenBindingDisposable(TokenResourceBinder.CreateTokenBinding(this, SpinnerHandleWidthTokenProperty, + ButtonSpinnerTokenKey.HandleWidth)); } } \ No newline at end of file diff --git a/src/AtomUI.Controls/ComboBox/ComboBoxTheme.cs b/src/AtomUI.Controls/ComboBox/ComboBoxTheme.cs index d75d656d3..bc69c0761 100644 --- a/src/AtomUI.Controls/ComboBox/ComboBoxTheme.cs +++ b/src/AtomUI.Controls/ComboBox/ComboBoxTheme.cs @@ -101,10 +101,12 @@ internal class ComboBoxTheme : BaseControlTheme var decreaseButtonIcon = AntDesignIconPackage.DownOutlined(); - TokenResourceBinder.CreateTokenBinding(decreaseButtonIcon, Icon.ActiveFilledBrushProperty, - ButtonSpinnerTokenKey.HandleHoverColor); - TokenResourceBinder.CreateTokenBinding(decreaseButtonIcon, Icon.SelectedFilledBrushProperty, - SharedTokenKey.ColorPrimaryActive); + comboBox.AddTokenBindingDisposable(TokenResourceBinder.CreateTokenBinding(decreaseButtonIcon, + Icon.ActiveFilledBrushProperty, + ButtonSpinnerTokenKey.HandleHoverColor)); + comboBox.AddTokenBindingDisposable(TokenResourceBinder.CreateTokenBinding(decreaseButtonIcon, + Icon.SelectedFilledBrushProperty, + SharedTokenKey.ColorPrimaryActive)); var openButton = new IconButton { @@ -117,12 +119,14 @@ internal class ComboBoxTheme : BaseControlTheme openButton.RegisterInNameScope(scope); - TokenResourceBinder.CreateTokenBinding(openButton, Layoutable.WidthProperty, - ComboBoxTokenKey.OpenIndicatorWidth); - TokenResourceBinder.CreateTokenBinding(openButton, IconButton.IconWidthProperty, - SharedTokenKey.IconSizeSM); - TokenResourceBinder.CreateTokenBinding(openButton, IconButton.IconHeightProperty, - SharedTokenKey.IconSizeSM); + comboBox.AddTokenBindingDisposable(TokenResourceBinder.CreateTokenBinding(openButton, Layoutable.WidthProperty, + ComboBoxTokenKey.OpenIndicatorWidth)); + comboBox.AddTokenBindingDisposable(TokenResourceBinder.CreateTokenBinding(openButton, + IconButton.IconWidthProperty, + SharedTokenKey.IconSizeSM)); + comboBox.AddTokenBindingDisposable(TokenResourceBinder.CreateTokenBinding(openButton, + IconButton.IconHeightProperty, + SharedTokenKey.IconSizeSM)); spinnerHandleDecorator.Child = openButton; spinnerInnerBox.SpinnerContent = spinnerHandleDecorator; @@ -179,12 +183,12 @@ internal class ComboBoxTheme : BaseControlTheme var border = new Border(); - TokenResourceBinder.CreateTokenBinding(border, Border.BackgroundProperty, - SharedTokenKey.ColorBgContainer); - TokenResourceBinder.CreateTokenBinding(border, Border.CornerRadiusProperty, - ComboBoxTokenKey.PopupBorderRadius); - TokenResourceBinder.CreateTokenBinding(border, Decorator.PaddingProperty, - ComboBoxTokenKey.PopupContentPadding); + comboBox.AddTokenBindingDisposable(TokenResourceBinder.CreateTokenBinding(border, Border.BackgroundProperty, + SharedTokenKey.ColorBgContainer)); + comboBox.AddTokenBindingDisposable(TokenResourceBinder.CreateTokenBinding(border, Border.CornerRadiusProperty, + ComboBoxTokenKey.PopupBorderRadius)); + comboBox.AddTokenBindingDisposable(TokenResourceBinder.CreateTokenBinding(border, Decorator.PaddingProperty, + ComboBoxTokenKey.PopupContentPadding)); var scrollViewer = new MenuScrollViewer(); var itemsPresenter = new ItemsPresenter @@ -198,10 +202,10 @@ internal class ComboBoxTheme : BaseControlTheme popup.Child = border; - TokenResourceBinder.CreateTokenBinding(popup, Popup.MarginToAnchorProperty, - ComboBoxTokenKey.PopupMarginToAnchor); - TokenResourceBinder.CreateTokenBinding(popup, Popup.MaskShadowsProperty, - ComboBoxTokenKey.PopupBoxShadows); + comboBox.AddTokenBindingDisposable(TokenResourceBinder.CreateTokenBinding(popup, Popup.MarginToAnchorProperty, + ComboBoxTokenKey.PopupMarginToAnchor)); + comboBox.AddTokenBindingDisposable(TokenResourceBinder.CreateTokenBinding(popup, Popup.MaskShadowsProperty, + ComboBoxTokenKey.PopupBoxShadows)); CreateTemplateParentBinding(popup, Layoutable.MaxHeightProperty, ComboBox.MaxDropDownHeightProperty); CreateTemplateParentBinding(popup, Avalonia.Controls.Primitives.Popup.IsOpenProperty,