From aab88394b4e046f4455181b4fffc4b14407da723 Mon Sep 17 00:00:00 2001 From: Dirkster99 Date: Sat, 22 Feb 2020 19:00:46 +0100 Subject: [PATCH 1/4] Fixed ChildrenTreeChanged and PropertyChanged event leaks in LayoutGridControl --- .../AvalonDock/Controls/LayoutGridControl.cs | 69 +++++++++++++------ 1 file changed, 47 insertions(+), 22 deletions(-) diff --git a/source/Components/AvalonDock/Controls/LayoutGridControl.cs b/source/Components/AvalonDock/Controls/LayoutGridControl.cs index af3b3eaa..7eff6158 100644 --- a/source/Components/AvalonDock/Controls/LayoutGridControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutGridControl.cs @@ -42,10 +42,15 @@ public abstract class LayoutGridControl : Grid, ILayoutControl, IAdjustableSi private Border _resizerGhost = null; private Window _resizerWindowHost = null; private Vector _initialStartPoint; + private bool _InitEvents; #endregion fields #region Constructors - + /// + /// Class constructor + /// + /// + /// internal LayoutGridControl(LayoutPositionableGroup model, Orientation orientation) { _model = model ?? throw new ArgumentNullException(nameof(model)); @@ -67,24 +72,18 @@ internal LayoutGridControl(LayoutPositionableGroup model, Orientation orienta #endregion #region Overrides - + /// protected override void OnInitialized(EventArgs e) { base.OnInitialized(e); - _model.ChildrenTreeChanged += (s, args) => - { - if (args.Change != ChildrenTreeChange.DirectChildrenChanged) return; - if (_asyncRefreshCalled.HasValue && _asyncRefreshCalled.Value == args.Change) return; - _asyncRefreshCalled = args.Change; - Dispatcher.BeginInvoke(new Action(() => - { - _asyncRefreshCalled = null; - UpdateChildren(); - }), DispatcherPriority.Normal, null); - }; - this.SizeChanged += OnSizeChanged; - } + if (_InitEvents == false) // We'll do this only once even for multiple inits + { + _InitEvents = true; + _model.ChildrenTreeChanged += _model_ChildrenTreeChanged; + this.SizeChanged += OnSizeChanged; + } + } #endregion #region Internal Methods @@ -100,6 +99,39 @@ protected void FixChildrenDockLengths() #endregion #region Private Methods + /// + /// Mehtod executes when the element is removed from within an element tree of loaded elements. + /// + /// + /// + private void OnUnloaded(object sender, RoutedEventArgs e) + { + // In order to prevent resource leaks, unsubscribe from subscribed events... + SizeChanged -= OnSizeChanged; + _model.ChildrenTreeChanged -= _model_ChildrenTreeChanged; + + DetachOldSplitters(); + DetachPropertyChangeHandler(); // Including property changed event handlers + Children.Clear(); + ColumnDefinitions.Clear(); + RowDefinitions.Clear(); + + Unloaded -= OnUnloaded; + } + + private void _model_ChildrenTreeChanged(object sender, ChildrenTreeChangedEventArgs e) + { + { + if (e.Change != ChildrenTreeChange.DirectChildrenChanged) return; + if (_asyncRefreshCalled.HasValue && _asyncRefreshCalled.Value == e.Change) return; + _asyncRefreshCalled = e.Change; + Dispatcher.BeginInvoke(new Action(() => + { + _asyncRefreshCalled = null; + UpdateChildren(); + }), DispatcherPriority.Normal, null); + }; + } private void OnSizeChanged(object sender, SizeChangedEventArgs e) { @@ -114,13 +146,6 @@ private void OnSizeChanged(object sender, SizeChangedEventArgs e) AdjustFixedChildrenPanelSizes(); } - private void OnUnloaded(object sender, RoutedEventArgs e) - { - // In order to prevent resource leaks, unsubscribe from SizeChanged events. - SizeChanged -= OnSizeChanged; - Unloaded -= OnUnloaded; - } - private void UpdateChildren() { var alreadyContainedChildren = Children.OfType().ToArray(); From c01911cce21655b49a1727990360257efb838570 Mon Sep 17 00:00:00 2001 From: Dirkster99 Date: Sat, 22 Feb 2020 19:03:18 +0100 Subject: [PATCH 2/4] Update LayoutGridControl.cs --- source/Components/AvalonDock/Controls/LayoutGridControl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/Components/AvalonDock/Controls/LayoutGridControl.cs b/source/Components/AvalonDock/Controls/LayoutGridControl.cs index 7eff6158..37129297 100644 --- a/source/Components/AvalonDock/Controls/LayoutGridControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutGridControl.cs @@ -100,7 +100,7 @@ protected void FixChildrenDockLengths() #region Private Methods /// - /// Mehtod executes when the element is removed from within an element tree of loaded elements. + /// Method executes when the element is removed from within an element tree of loaded elements. /// /// /// From 06c32ad2deb690766e2d44948501c37ab24cd7dc Mon Sep 17 00:00:00 2001 From: Dirkster99 Date: Sat, 22 Feb 2020 19:29:54 +0100 Subject: [PATCH 3/4] Fixing Model.PropertyChanged memory leak via event handler in LayoutAnchorableControl --- .../Controls/LayoutAnchorableControl.cs | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/source/Components/AvalonDock/Controls/LayoutAnchorableControl.cs b/source/Components/AvalonDock/Controls/LayoutAnchorableControl.cs index 1828ca2f..eb3e23ea 100644 --- a/source/Components/AvalonDock/Controls/LayoutAnchorableControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutAnchorableControl.cs @@ -19,18 +19,23 @@ namespace AvalonDock.Controls public class LayoutAnchorableControl : Control { #region Constructors - + /// + /// Static class constructor + /// static LayoutAnchorableControl() { DefaultStyleKeyProperty.OverrideMetadata(typeof(LayoutAnchorableControl), new FrameworkPropertyMetadata(typeof(LayoutAnchorableControl))); FocusableProperty.OverrideMetadata(typeof(LayoutAnchorableControl), new FrameworkPropertyMetadata(false)); } + /// + /// Class constructor + /// public LayoutAnchorableControl() { //SetBinding(FlowDirectionProperty, new Binding("Model.Root.Manager.FlowDirection") { Source = this }); + Unloaded += LayoutAnchorableControl_Unloaded; } - #endregion Constructors #region Properties @@ -98,8 +103,8 @@ private void Model_PropertyChanged(object sender, System.ComponentModel.Property #endregion Properties - #region Overrides - + #region Methods + /// protected override void OnGotKeyboardFocus(System.Windows.Input.KeyboardFocusChangedEventArgs e) { if (Model != null) @@ -107,6 +112,19 @@ protected override void OnGotKeyboardFocus(System.Windows.Input.KeyboardFocusCha base.OnGotKeyboardFocus(e); } - #endregion Overrides + /// + /// Executes when the element is removed from within an element tree of loaded elements. + /// + /// + /// + private void LayoutAnchorableControl_Unloaded(object sender, RoutedEventArgs e) + { + // prevent memory leak via event handler + if (Model != null) + Model.PropertyChanged -= Model_PropertyChanged; + + Unloaded -= LayoutAnchorableControl_Unloaded; + } + #endregion Methods } } From 1c59b3300d6d67f2eac4e700d1cfb95ca38a3ff0 Mon Sep 17 00:00:00 2001 From: Dirkster99 Date: Sat, 22 Feb 2020 19:41:23 +0100 Subject: [PATCH 4/4] Fixed memory leak via CollectionChanged event in LayoutAnchorSideControl --- .../Controls/LayoutAnchorSideControl.cs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/source/Components/AvalonDock/Controls/LayoutAnchorSideControl.cs b/source/Components/AvalonDock/Controls/LayoutAnchorSideControl.cs index 1f9cbeb7..cba3ecce 100644 --- a/source/Components/AvalonDock/Controls/LayoutAnchorSideControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutAnchorSideControl.cs @@ -43,10 +43,11 @@ internal LayoutAnchorSideControl(LayoutAnchorSide model) { _model = model ?? throw new ArgumentNullException(nameof(model)); CreateChildrenViews(); - _model.Children.CollectionChanged += (s, e) => OnModelChildrenCollectionChanged(e); + _model.Children.CollectionChanged += OnModelChildrenCollectionChanged; UpdateSide(); - } + Unloaded += LayoutAnchorSideControl_Unloaded; + } #endregion Constructors #region Properties @@ -126,6 +127,16 @@ internal LayoutAnchorSideControl(LayoutAnchorSide model) #endregion Properties #region Private Methods + /// + /// Executes when the element is removed from within an element tree of loaded elements. + /// + /// + /// + private void LayoutAnchorSideControl_Unloaded(object sender, RoutedEventArgs e) + { + _model.Children.CollectionChanged -= OnModelChildrenCollectionChanged; + Unloaded -= LayoutAnchorSideControl_Unloaded; + } private void CreateChildrenViews() { @@ -133,7 +144,8 @@ private void CreateChildrenViews() foreach (var childModel in _model.Children) _childViews.Add(manager.CreateUIElementForModel(childModel) as LayoutAnchorGroupControl); } - private void OnModelChildrenCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e) + private void OnModelChildrenCollectionChanged(object sender, + System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { if (e.OldItems != null && (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Remove ||