Browse Source

Think I've fixed the leak...

But I think it's showing up bugs elsewhere that need to be fixed.
pull/467/head
Steven Kirk 11 years ago
parent
commit
1ae6b929b3
  1. 6
      src/Perspex.Controls/Control.cs
  2. 12
      src/Perspex.Controls/Primitives/TemplatedControl.cs
  3. 10
      src/Perspex.Styling/LogicalTree/ILogical.cs
  4. 18
      tests/Perspex.Controls.UnitTests/ControlTests.cs
  5. 46
      tests/Perspex.Controls.UnitTests/Primitives/TemplatedControlTests.cs
  6. 5
      tests/Perspex.Styling.UnitTests/SelectorTests_Child.cs
  7. 5
      tests/Perspex.Styling.UnitTests/SelectorTests_Descendent.cs
  8. 26
      tests/Perspex.Styling.UnitTests/SelectorTests_Template.cs

6
src/Perspex.Controls/Control.cs

@ -312,6 +312,12 @@ namespace Perspex.Controls
/// <inheritdoc/> /// <inheritdoc/>
IStyleHost IStyleHost.StylingParent => (IStyleHost)InheritanceParent; IStyleHost IStyleHost.StylingParent => (IStyleHost)InheritanceParent;
/// <inheritdoc/>
void ILogical.NotifyDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
{
this.OnDetachedFromLogicalTree(e);
}
/// <inheritdoc/> /// <inheritdoc/>
void ISupportInitialize.BeginInit() void ISupportInitialize.BeginInit()
{ {

12
src/Perspex.Controls/Primitives/TemplatedControl.cs

@ -7,6 +7,7 @@ using System.Reactive.Linq;
using Perspex.Controls.Templates; using Perspex.Controls.Templates;
using Perspex.Data; using Perspex.Data;
using Perspex.Interactivity; using Perspex.Interactivity;
using Perspex.LogicalTree;
using Perspex.Media; using Perspex.Media;
using Perspex.Styling; using Perspex.Styling;
using Perspex.VisualTree; using Perspex.VisualTree;
@ -311,6 +312,17 @@ namespace Perspex.Controls.Primitives
return this; return this;
} }
/// <inheritdoc/>
protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
{
if (VisualChildren.Count > 0)
{
((ILogical)VisualChildren[0]).NotifyDetachedFromLogicalTree(e);
}
base.OnDetachedFromLogicalTree(e);
}
/// <summary> /// <summary>
/// Called when the control's template is applied. /// Called when the control's template is applied.
/// </summary> /// </summary>

10
src/Perspex.Styling/LogicalTree/ILogical.cs

@ -35,5 +35,15 @@ namespace Perspex.LogicalTree
/// Gets the logical children. /// Gets the logical children.
/// </summary> /// </summary>
IPerspexReadOnlyList<ILogical> LogicalChildren { get; } IPerspexReadOnlyList<ILogical> LogicalChildren { get; }
/// <summary>
/// Notifies the control that it is being detached from a rooted logical tree.
/// </summary>
/// <param name="e">The event args.</param>
/// <remarks>
/// This method will be called automatically by the framework, you should not need to call
/// this method yourself.
/// </remarks>
void NotifyDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e);
} }
} }

18
tests/Perspex.Controls.UnitTests/ControlTests.cs

@ -212,6 +212,24 @@ namespace Perspex.Controls.UnitTests
} }
} }
[Fact]
public void StyleDetach_Is_Triggered_When_Control_Removed_From_Logical_Tree()
{
using (PerspexLocator.EnterScope())
{
var root = new TestRoot();
var child = new Border();
root.Child = child;
bool styleDetachTriggered = false;
((IStyleable)child).StyleDetach.Subscribe(_ => styleDetachTriggered = true);
root.Child = null;
Assert.True(styleDetachTriggered);
}
}
private class TestControl : Control private class TestControl : Control
{ {
public new PerspexObject InheritanceParent => base.InheritanceParent; public new PerspexObject InheritanceParent => base.InheritanceParent;

46
tests/Perspex.Controls.UnitTests/Primitives/TemplatedControlTests.cs

@ -344,6 +344,52 @@ namespace Perspex.Controls.UnitTests.Primitives
Assert.Null(border.TemplatedParent); Assert.Null(border.TemplatedParent);
} }
[Fact]
public void TemplateChild_AttachedToLogicalTree_Should_Be_Raised()
{
Border templateChild = new Border();
var root = new TestRoot
{
Child = new TestTemplatedControl
{
Template = new FuncControlTemplate(_ => new Decorator
{
Child = templateChild,
})
}
};
var raised = false;
templateChild.AttachedToLogicalTree += (s, e) => raised = true;
root.Child.ApplyTemplate();
Assert.True(raised);
}
[Fact]
public void TemplateChild_DetachedFromLogicalTree_Should_Be_Raised()
{
Border templateChild = new Border();
var root = new TestRoot
{
Child = new TestTemplatedControl
{
Template = new FuncControlTemplate(_ => new Decorator
{
Child = templateChild,
})
}
};
root.Child.ApplyTemplate();
var raised = false;
templateChild.DetachedFromLogicalTree += (s, e) => raised = true;
root.Child = null;
Assert.True(raised);
}
private static IControl ScrollingContentControlTemplate(ContentControl control) private static IControl ScrollingContentControlTemplate(ContentControl control)
{ {
return new Border return new Border

5
tests/Perspex.Styling.UnitTests/SelectorTests_Child.cs

@ -135,6 +135,11 @@ namespace Perspex.Styling.UnitTests
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public void NotifyDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
{
throw new NotImplementedException();
}
} }
public class TestLogical1 : TestLogical public class TestLogical1 : TestLogical

5
tests/Perspex.Styling.UnitTests/SelectorTests_Descendent.cs

@ -166,6 +166,11 @@ namespace Perspex.Styling.UnitTests
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public void NotifyDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
{
throw new NotImplementedException();
}
} }
public class TestLogical1 : TestLogical public class TestLogical1 : TestLogical

26
tests/Perspex.Styling.UnitTests/SelectorTests_Template.cs

@ -1,6 +1,7 @@
// Copyright (c) The Perspex Project. All rights reserved. // Copyright (c) The Perspex Project. All rights reserved.
// 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.Linq; using System.Linq;
using System.Reactive.Linq; using System.Reactive.Linq;
using Moq; using Moq;
@ -11,6 +12,9 @@ using System.Threading.Tasks;
namespace Perspex.Styling.UnitTests namespace Perspex.Styling.UnitTests
{ {
using System.Reactive;
using System.Reactive.Subjects;
using Collections;
using Controls = Controls.Controls; using Controls = Controls.Controls;
public class SelectorTests_Template public class SelectorTests_Template
@ -108,6 +112,28 @@ namespace Perspex.Styling.UnitTests
Assert.False(await activator.Take(1)); Assert.False(await activator.Take(1));
} }
[Fact]
public void Nested_Selector_Is_Unsubscribed()
{
var target = new Mock<IVisual>();
var templatedControl = target.As<ITemplatedControl>();
var styleable = target.As<IStyleable>();
BuildVisualTree(target);
styleable.Setup(x => x.Classes).Returns(new Classes("foo"));
var border = (Border)target.Object.VisualChildren.Single();
var selector = new Selector().OfType(templatedControl.Object.GetType()).Class("foo").Template().OfType<Border>();
var activator = selector.Match(border).ObservableResult;
var inccDebug = (InccDebug)styleable.Object.Classes;
using (activator.Subscribe(_ => { }))
{
Assert.Equal(1, inccDebug.GetCollectionChangedSubscribers().Length);
}
Assert.Null(inccDebug.GetCollectionChangedSubscribers());
}
private void BuildVisualTree<T>(Mock<T> templatedControl) where T : class, IVisual private void BuildVisualTree<T>(Mock<T> templatedControl) where T : class, IVisual
{ {
templatedControl.Setup(x => x.VisualChildren).Returns(new Controls templatedControl.Setup(x => x.VisualChildren).Returns(new Controls

Loading…
Cancel
Save