Skip to content

Commit

Permalink
Merge pull request #128 from Dirkster99/FixingMemoryLeaks
Browse files Browse the repository at this point in the history
Fixing memory leaks
  • Loading branch information
Dirkster99 authored Feb 22, 2020
2 parents 0cafeee + 1c59b33 commit 53b6e3d
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 30 deletions.
18 changes: 15 additions & 3 deletions source/Components/AvalonDock/Controls/LayoutAnchorSideControl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -126,14 +127,25 @@ internal LayoutAnchorSideControl(LayoutAnchorSide model)
#endregion Properties

#region Private Methods
/// <summary>
/// Executes when the element is removed from within an element tree of loaded elements.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void LayoutAnchorSideControl_Unloaded(object sender, RoutedEventArgs e)
{
_model.Children.CollectionChanged -= OnModelChildrenCollectionChanged;
Unloaded -= LayoutAnchorSideControl_Unloaded;
}

private void CreateChildrenViews()
{
var manager = _model.Root.Manager;
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 ||
Expand Down
28 changes: 23 additions & 5 deletions source/Components/AvalonDock/Controls/LayoutAnchorableControl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,23 @@ namespace AvalonDock.Controls
public class LayoutAnchorableControl : Control
{
#region Constructors

/// <summary>
/// Static class constructor
/// </summary>
static LayoutAnchorableControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(LayoutAnchorableControl), new FrameworkPropertyMetadata(typeof(LayoutAnchorableControl)));
FocusableProperty.OverrideMetadata(typeof(LayoutAnchorableControl), new FrameworkPropertyMetadata(false));
}

/// <summary>
/// Class constructor
/// </summary>
public LayoutAnchorableControl()
{
//SetBinding(FlowDirectionProperty, new Binding("Model.Root.Manager.FlowDirection") { Source = this });
Unloaded += LayoutAnchorableControl_Unloaded;
}

#endregion Constructors

#region Properties
Expand Down Expand Up @@ -98,15 +103,28 @@ private void Model_PropertyChanged(object sender, System.ComponentModel.Property

#endregion Properties

#region Overrides

#region Methods
/// <inheritdoc/>
protected override void OnGotKeyboardFocus(System.Windows.Input.KeyboardFocusChangedEventArgs e)
{
if (Model != null)
Model.IsActive = true;
base.OnGotKeyboardFocus(e);
}

#endregion Overrides
/// <summary>
/// Executes when the element is removed from within an element tree of loaded elements.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
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
}
}
69 changes: 47 additions & 22 deletions source/Components/AvalonDock/Controls/LayoutGridControl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,15 @@ public abstract class LayoutGridControl<T> : Grid, ILayoutControl, IAdjustableSi
private Border _resizerGhost = null;
private Window _resizerWindowHost = null;
private Vector _initialStartPoint;
private bool _InitEvents;
#endregion fields

#region Constructors

/// <summary>
/// Class constructor
/// </summary>
/// <param name="model"></param>
/// <param name="orientation"></param>
internal LayoutGridControl(LayoutPositionableGroup<T> model, Orientation orientation)
{
_model = model ?? throw new ArgumentNullException(nameof(model));
Expand All @@ -67,24 +72,18 @@ internal LayoutGridControl(LayoutPositionableGroup<T> model, Orientation orienta
#endregion

#region Overrides

/// <inheritdoc/>
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
Expand All @@ -100,6 +99,39 @@ protected void FixChildrenDockLengths()
#endregion

#region Private Methods
/// <summary>
/// Method executes when the element is removed from within an element tree of loaded elements.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
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)
{
Expand All @@ -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<ILayoutControl>().ToArray();
Expand Down

0 comments on commit 53b6e3d

Please sign in to comment.