Browse Source

Fixed name scopes.

Controls were being registered with the nearest name scope on the visual
tree, whereas they should be registered by traversing the logical tree.
pull/335/head
Steven Kirk 10 years ago
parent
commit
9eb54f59d2
  1. 4
      src/Markup/Perspex.Markup.Xaml/Context/NameScopeWrapper.cs
  2. 10
      src/Markup/Perspex.Markup.Xaml/Context/PerspexXamlType.cs
  3. 2
      src/Markup/Perspex.Markup/ControlLocator.cs
  4. 2
      src/Perspex.Base/PerspexProperty.cs
  5. 12
      src/Perspex.Controls/Button.cs
  6. 31
      src/Perspex.Controls/Control.cs
  7. 4
      src/Perspex.Controls/ControlExtensions.cs
  8. 2
      src/Perspex.Controls/Decorator.cs
  9. 2
      src/Perspex.Controls/INameScope.cs
  10. 8
      src/Perspex.Controls/NameScope.cs
  11. 2
      src/Perspex.Controls/NameScopeEventArgs.cs
  12. 2
      src/Perspex.Controls/NameScopeExtensions.cs
  13. 4
      src/Perspex.Controls/Perspex.Controls.csproj
  14. 5
      src/Perspex.Controls/Presenters/ContentPresenter.cs
  15. 2
      src/Perspex.Controls/Primitives/TemplatedControl.cs
  16. 4
      src/Perspex.SceneGraph/Perspex.SceneGraph.csproj
  17. 87
      src/Perspex.SceneGraph/Visual.cs
  18. 9
      src/Perspex.SceneGraph/VisualTreeAttachmentEventArgs.cs
  19. 125
      tests/Perspex.Controls.UnitTests/ControlTests_NameScope.cs
  20. 2
      tests/Perspex.Controls.UnitTests/NameScopeTests.cs
  21. 2
      tests/Perspex.Controls.UnitTests/Perspex.Controls.UnitTests.csproj
  22. 2
      tests/Perspex.Controls.UnitTests/Primitives/TemplatedControlTests.cs
  23. 3
      tests/Perspex.SceneGraph.UnitTests/Perspex.SceneGraph.UnitTests.csproj
  24. 31
      tests/Perspex.SceneGraph.UnitTests/TestRoot.cs
  25. 50
      tests/Perspex.SceneGraph.UnitTests/VisualTests.cs

4
src/Markup/Perspex.Markup.Xaml/Context/NameScopeWrapper.cs

@ -5,9 +5,9 @@ namespace Perspex.Markup.Xaml.Context
{
internal class NameScopeWrapper : OmniXaml.INameScope
{
private Perspex.INameScope _inner;
private readonly Perspex.Controls.INameScope _inner;
public NameScopeWrapper(Perspex.INameScope inner)
public NameScopeWrapper(Perspex.Controls.INameScope inner)
{
_inner = inner;
}

10
src/Markup/Perspex.Markup.Xaml/Context/PerspexXamlType.cs

@ -5,7 +5,7 @@ using System;
using System.Reflection;
using OmniXaml;
using OmniXaml.Typing;
using Perspex.Markup.Xaml.Data;
using Perspex.Controls;
namespace Perspex.Markup.Xaml.Context
{
@ -20,15 +20,15 @@ namespace Perspex.Markup.Xaml.Context
public override OmniXaml.INameScope GetNamescope(object instance)
{
var result = this.UnderlyingType as OmniXaml.INameScope;
var result = instance as OmniXaml.INameScope;
if (result == null)
{
var visual = instance as Visual;
var control = instance as Control;
if (visual != null)
if (control != null)
{
var perspexNs = (instance as Perspex.INameScope) ?? NameScope.GetNameScope(visual);
var perspexNs = (instance as Perspex.Controls.INameScope) ?? NameScope.GetNameScope(control);
if (perspexNs != null)
{

2
src/Markup/Perspex.Markup/ControlLocator.cs

@ -25,7 +25,7 @@ namespace Perspex.Markup
var attached = Observable.FromEventPattern<VisualTreeAttachmentEventArgs>(
x => relativeTo.AttachedToVisualTree += x,
x => relativeTo.DetachedFromVisualTree += x)
.Select(x => x.EventArgs.NameScope)
.Select(x => ((IControl)x.Sender).FindNameScope())
.StartWith(relativeTo.FindNameScope());
var detached = Observable.FromEventPattern<VisualTreeAttachmentEventArgs>(

2
src/Perspex.Base/PerspexProperty.cs

@ -56,7 +56,7 @@ namespace Perspex
/// <summary>
/// Gets the ID of the property.
/// </summary>
private int _id;
private readonly int _id;
/// <summary>
/// Initializes a new instance of the <see cref="PerspexProperty"/> class.

12
src/Perspex.Controls/Button.cs

@ -134,18 +134,6 @@ namespace Perspex.Controls
set { SetValue(IsDefaultProperty, value); }
}
/// <inheritdoc/>
protected override Size MeasureOverride(Size availableSize)
{
return base.MeasureOverride(availableSize);
}
/// <inheritdoc/>
protected override Size ArrangeOverride(Size finalSize)
{
return base.ArrangeOverride(finalSize);
}
/// <inheritdoc/>
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{

31
src/Perspex.Controls/Control.cs

@ -71,6 +71,7 @@ namespace Perspex.Controls
private DataTemplates _dataTemplates;
private IControl _focusAdorner;
private IPerspexList<ILogical> _logicalChildren;
private INameScope _nameScope;
private Styles _styles;
/// <summary>
@ -84,6 +85,14 @@ namespace Perspex.Controls
PseudoClass(IsPointerOverProperty, ":pointerover");
}
/// <summary>
/// Initializes a new instance of the <see cref="Control"/> class.
/// </summary>
public Control()
{
_nameScope = this as INameScope;
}
/// <summary>
/// Gets or sets the control's classes.
/// </summary>
@ -376,11 +385,27 @@ namespace Perspex.Controls
{
base.OnAttachedToVisualTree(e);
IStyler styler = PerspexLocator.Current.GetService<IStyler>();
if (_nameScope == null)
{
_nameScope = NameScope.GetNameScope(this) ?? ((Control)Parent)?._nameScope;
}
if (Name != null)
{
_nameScope?.Register(Name, this);
}
PerspexLocator.Current.GetService<IStyler>()?.ApplyStyles(this);
}
/// <inheritdoc/>
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnDetachedFromVisualTree(e);
if (styler != null)
if (Name != null)
{
styler.ApplyStyles(this);
_nameScope?.Unregister(Name);
}
}

4
src/Perspex.Controls/ControlExtensions.cs

@ -59,14 +59,14 @@ namespace Perspex.Controls
}
/// <summary>
/// Finds the name scope for a control.
/// Finds the name scope for a control by searching up the logical tree.
/// </summary>
/// <param name="control">The control.</param>
/// <returns>The control's name scope, or null if not found.</returns>
public static INameScope FindNameScope(this IControl control)
{
return control.GetSelfAndLogicalAncestors()
.OfType<Visual>()
.OfType<Control>()
.Select(x => (x as INameScope) ?? NameScope.GetNameScope(x))
.FirstOrDefault(x => x != null);
}

2
src/Perspex.Controls/Decorator.cs

@ -98,9 +98,9 @@ namespace Perspex.Controls
if (newChild != null)
{
((ISetLogicalParent)newChild).SetParent(this);
AddVisualChild(newChild);
LogicalChildren.Add(newChild);
((ISetLogicalParent)newChild).SetParent(this);
}
}
}

2
src/Perspex.SceneGraph/INameScope.cs → src/Perspex.Controls/INameScope.cs

@ -3,7 +3,7 @@
using System;
namespace Perspex
namespace Perspex.Controls
{
/// <summary>
/// Defines a name scope.

8
src/Perspex.SceneGraph/NameScope.cs → src/Perspex.Controls/NameScope.cs

@ -4,7 +4,7 @@
using System;
using System.Collections.Generic;
namespace Perspex
namespace Perspex.Controls
{
/// <summary>
/// Implements a name scope.
@ -15,7 +15,7 @@ namespace Perspex
/// Defines the NameScope attached property.
/// </summary>
public static readonly PerspexProperty<INameScope> NameScopeProperty =
PerspexProperty.RegisterAttached<NameScope, Visual, INameScope>("NameScope");
PerspexProperty.RegisterAttached<NameScope, Control, INameScope>("NameScope");
private readonly Dictionary<string, object> _inner = new Dictionary<string, object>();
@ -34,7 +34,7 @@ namespace Perspex
/// </summary>
/// <param name="visual">The visual.</param>
/// <returns>The value of the NameScope attached property.</returns>
public static INameScope GetNameScope(Visual visual)
public static INameScope GetNameScope(Control visual)
{
return visual.GetValue(NameScopeProperty);
}
@ -44,7 +44,7 @@ namespace Perspex
/// </summary>
/// <param name="visual">The visual.</param>
/// <param name="value">The value to set.</param>
public static void SetNameScope(Visual visual, INameScope value)
public static void SetNameScope(Control visual, INameScope value)
{
visual.SetValue(NameScopeProperty, value);
}

2
src/Perspex.SceneGraph/NameScopeEventArgs.cs → src/Perspex.Controls/NameScopeEventArgs.cs

@ -3,7 +3,7 @@
using System;
namespace Perspex
namespace Perspex.Controls
{
public class NameScopeEventArgs : EventArgs
{

2
src/Perspex.SceneGraph/NameScopeExtensions.cs → src/Perspex.Controls/NameScopeExtensions.cs

@ -4,7 +4,7 @@
using System;
using System.Collections.Generic;
namespace Perspex
namespace Perspex.Controls
{
/// <summary>
/// Extension methods for <see cref="INameScope"/>.

4
src/Perspex.Controls/Perspex.Controls.csproj

@ -43,6 +43,10 @@
</Compile>
<Compile Include="DockPanel.cs" />
<Compile Include="HotkeyManager.cs" />
<Compile Include="INameScope.cs" />
<Compile Include="NameScope.cs" />
<Compile Include="NameScopeEventArgs.cs" />
<Compile Include="NameScopeExtensions.cs" />
<Compile Include="Platform\ITopLevelRenderer.cs" />
<Compile Include="Platform\IWindowingPlatform.cs" />
<Compile Include="Platform\PlatformManager.cs" />

5
src/Perspex.Controls/Presenters/ContentPresenter.cs

@ -106,8 +106,8 @@ namespace Perspex.Controls.Presenters
if (old != null)
{
logicalChildren.Remove(old);
((ISetLogicalParent)old).SetParent(null);
logicalChildren.Remove(old);
ClearVisualChildren();
}
@ -120,13 +120,12 @@ namespace Perspex.Controls.Presenters
result.DataContext = content;
}
AddVisualChild(result);
if (result.Parent == null)
{
((ISetLogicalParent)result).SetParent((ILogical)logicalHost ?? this);
}
AddVisualChild(result);
logicalChildren.Remove(old);
logicalChildren.Add(result);
}

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

@ -196,7 +196,7 @@ namespace Perspex.Controls.Primitives
var child = Template.Build(this);
var nameScope = new NameScope();
NameScope.SetNameScope((Visual)child, nameScope);
NameScope.SetNameScope((Control)child, nameScope);
// We need to call SetTemplatedParentAndApplyChildTemplates twice - once
// before the controls are added to the visual tree so that the logical

4
src/Perspex.SceneGraph/Perspex.SceneGraph.csproj

@ -56,7 +56,6 @@
<Compile Include="Animation\CrossFade.cs" />
<Compile Include="Animation\IPageTransition.cs" />
<Compile Include="INamed.cs" />
<Compile Include="INameScope.cs" />
<Compile Include="Matrix.cs" />
<Compile Include="Media\AlignmentY.cs" />
<Compile Include="Media\AlignmentX.cs" />
@ -103,9 +102,6 @@
<Compile Include="Media\TileBrush.cs" />
<Compile Include="Media\ImageBush.cs" />
<Compile Include="Media\VisualBrush.cs" />
<Compile Include="NameScopeEventArgs.cs" />
<Compile Include="NameScopeExtensions.cs" />
<Compile Include="NameScope.cs" />
<Compile Include="RelativePoint.cs" />
<Compile Include="Platform\IFormattedTextImpl.cs" />
<Compile Include="Platform\IBitmapImpl.cs" />

87
src/Perspex.SceneGraph/Visual.cs

@ -407,12 +407,7 @@ namespace Perspex
/// <param name="e">The event args.</param>
private static void AffectsRenderInvalidate(PerspexPropertyChangedEventArgs e)
{
Visual visual = e.Sender as Visual;
if (visual != null)
{
visual.InvalidateVisual();
}
(e.Sender as Visual)?.InvalidateVisual();
}
/// <summary>
@ -425,48 +420,8 @@ namespace Perspex
/// </returns>
private VisualTreeAttachmentEventArgs GetAttachmentEventArgs()
{
var e = (IVisual)this;
IRenderRoot root = null;
INameScope nameScope = null;
while (e != null)
{
if (nameScope == null)
{
nameScope = e as INameScope ?? NameScope.GetNameScope((Visual)e);
}
root = e as IRenderRoot;
if (root != null)
{
return new VisualTreeAttachmentEventArgs(root, nameScope);
}
e = e.VisualParent;
}
return null;
}
/// <summary>
/// Gets the <see cref="VisualTreeAttachmentEventArgs"/> for this element based on the
/// parent's args.
/// </summary>
/// <param name="e">The parent args.</param>
/// <returns>The args for this element.</returns>
private VisualTreeAttachmentEventArgs GetAttachmentEventArgs(VisualTreeAttachmentEventArgs e)
{
var childNameScope = (this as INameScope) ?? NameScope.GetNameScope(this);
if (childNameScope != null)
{
return new VisualTreeAttachmentEventArgs(e.Root, childNameScope);
}
else
{
return e;
}
var root = this.GetSelfAndVisualAncestors().OfType<IRenderRoot>().FirstOrDefault();
return root != null ? new VisualTreeAttachmentEventArgs(root) : null;
}
/// <summary>
@ -560,7 +515,14 @@ namespace Perspex
/// <param name="value">The visual parent.</param>
private void SetVisualParent(Visual value)
{
if (_visualParent != value)
if (_visualParent == value)
{
return;
}
var old = _visualParent;
if (_isAttachedToVisualTree)
{
var oldArgs = GetAttachmentEventArgs();
@ -570,16 +532,23 @@ namespace Perspex
{
NotifyDetachedFromVisualTree(oldArgs);
}
}
else
{
_visualParent = value;
}
if (_visualParent is IRenderRoot || _visualParent?.IsAttachedToVisualTree == true)
{
var newArgs = GetAttachmentEventArgs();
if (newArgs != null)
{
NotifyAttachedToVisualTree(newArgs);
}
RaisePropertyChanged(VisualParentProperty, oldArgs, value, BindingPriority.LocalValue);
}
RaisePropertyChanged(VisualParentProperty, old, value, BindingPriority.LocalValue);
}
/// <summary>
@ -622,19 +591,13 @@ namespace Perspex
_isAttachedToVisualTree = true;
if (Name != null && e.NameScope != null)
{
e.NameScope.Register(Name, this);
}
OnAttachedToVisualTree(e);
if (_visualChildren != null)
{
foreach (Visual child in _visualChildren.OfType<Visual>())
{
var ce = child.GetAttachmentEventArgs(e);
child.NotifyAttachedToVisualTree(ce);
child.NotifyAttachedToVisualTree(e);
}
}
}
@ -648,11 +611,6 @@ namespace Perspex
{
_visualLogger.Verbose("Detached from visual tree");
if (Name != null && e.NameScope != null)
{
e.NameScope.Unregister(Name);
}
_isAttachedToVisualTree = false;
OnDetachedFromVisualTree(e);
@ -660,8 +618,7 @@ namespace Perspex
{
foreach (Visual child in _visualChildren.OfType<Visual>())
{
var ce = child.GetAttachmentEventArgs(e);
child.NotifyDetachedFromVisualTree(ce);
child.NotifyDetachedFromVisualTree(e);
}
}
}

9
src/Perspex.SceneGraph/VisualTreeAttachmentEventArgs.cs

@ -16,21 +16,14 @@ namespace Perspex
/// Initializes a new instance of the <see cref="VisualTreeAttachmentEventArgs"/> class.
/// </summary>
/// <param name="root">The root visual.</param>
/// <param name="nameScope">The name scope.</param>
public VisualTreeAttachmentEventArgs(IRenderRoot root, INameScope nameScope)
public VisualTreeAttachmentEventArgs(IRenderRoot root)
{
Root = root;
NameScope = nameScope;
}
/// <summary>
/// Gets the root of the visual tree that the visual is being attached to or detached from.
/// </summary>
public IRenderRoot Root { get; }
/// <summary>
/// Gets the element's name scope.
/// </summary>
public INameScope NameScope { get; }
}
}

125
tests/Perspex.Controls.UnitTests/ControlTests_NameScope.cs

@ -0,0 +1,125 @@
// 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.
using System;
using Perspex.Controls.Presenters;
using Perspex.Controls.Templates;
using Perspex.Rendering;
using Xunit;
namespace Perspex.Controls.UnitTests
{
public class ControlTests_NameScope
{
[Fact]
public void Controls_Should_Register_With_NameScope()
{
var root = new TestRoot
{
Content = new Border
{
Name = "foo",
Child = new Border
{
Name = "bar",
}
}
};
root.ApplyTemplate();
Assert.Same(root.Find("foo"), root.Content);
Assert.Same(root.Find("bar"), ((Border)root.Content).Child);
}
[Fact]
public void Control_Should_Unregister_With_NameScope()
{
var root = new TestRoot
{
Content = new Border
{
Name = "foo",
Child = new Border
{
Name = "bar",
}
}
};
root.ApplyTemplate();
root.Content = null;
root.Presenter.ApplyTemplate();
Assert.Null(root.Find("foo"));
Assert.Null(root.Find("bar"));
}
[Fact]
public void Control_Should_Not_Register_With_Template_NameScope()
{
var root = new TestRoot
{
Content = new Border
{
Name = "foo",
}
};
root.ApplyTemplate();
Assert.Null(NameScope.GetNameScope(root.Presenter).Find("foo"));
}
private class TestRoot : ContentControl, IRenderRoot, INameScope
{
private readonly NameScope _nameScope = new NameScope();
public TestRoot()
{
Template = new FuncControlTemplate<TestRoot>(x => new ContentPresenter
{
Name = "PART_ContentPresenter",
[!ContentPresenter.ContentProperty] = x[!ContentControl.ContentProperty],
});
}
public event EventHandler<NameScopeEventArgs> Registered
{
add { _nameScope.Registered += value; }
remove { _nameScope.Registered -= value; }
}
public event EventHandler<NameScopeEventArgs> Unregistered
{
add { _nameScope.Unregistered += value; }
remove { _nameScope.Unregistered -= value; }
}
public IRenderQueueManager RenderQueueManager
{
get { throw new NotImplementedException(); }
}
public Point TranslatePointToScreen(Point p)
{
throw new NotImplementedException();
}
public void Register(string name, object element)
{
_nameScope.Register(name, element);
}
public object Find(string name)
{
return _nameScope.Find(name);
}
public void Unregister(string name)
{
_nameScope.Unregister(name);
}
}
}
}

2
tests/Perspex.SceneGraph.UnitTests/NameScopeTests.cs → tests/Perspex.Controls.UnitTests/NameScopeTests.cs

@ -4,7 +4,7 @@
using System;
using Xunit;
namespace Perspex.SceneGraph.UnitTests
namespace Perspex.Controls.UnitTests
{
public class NameScopeTests
{

2
tests/Perspex.Controls.UnitTests/Perspex.Controls.UnitTests.csproj

@ -81,12 +81,14 @@
<Otherwise />
</Choose>
<ItemGroup>
<Compile Include="ControlTests_NameScope.cs" />
<Compile Include="Generators\ItemContainerGeneratorTests.cs" />
<Compile Include="Generators\ItemContainerGeneratorTypedTests.cs" />
<Compile Include="GridLengthTests.cs" />
<Compile Include="ContentPresenterTests.cs" />
<Compile Include="BorderTests.cs" />
<Compile Include="ListBoxTests_Single.cs" />
<Compile Include="NameScopeTests.cs" />
<Compile Include="Primitives\SelectingItemsControlTests_Multiple.cs" />
<Compile Include="WindowingPlatformMock.cs" />
<Compile Include="TreeViewTests.cs" />

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

@ -70,7 +70,7 @@ namespace Perspex.Controls.UnitTests.Primitives
target.ApplyTemplate();
Assert.NotNull(NameScope.GetNameScope((Visual)target.GetVisualChildren().Single()));
Assert.NotNull(NameScope.GetNameScope((Control)target.GetVisualChildren().Single()));
}
[Fact]

3
tests/Perspex.SceneGraph.UnitTests/Perspex.SceneGraph.UnitTests.csproj

@ -72,7 +72,6 @@
<Otherwise />
</Choose>
<ItemGroup>
<Compile Include="NameScopeTests.cs" />
<Compile Include="RelativeRectComparer.cs" />
<Compile Include="RelativeRectTests.cs" />
<Compile Include="ThicknessTests.cs" />
@ -160,4 +159,4 @@
<Target Name="AfterBuild">
</Target>
-->
</Project>
</Project>

31
tests/Perspex.SceneGraph.UnitTests/TestRoot.cs

@ -7,22 +7,8 @@ using Perspex.Rendering;
namespace Perspex.SceneGraph.UnitTests
{
public class TestRoot : TestVisual, IRenderRoot, INameScope
public class TestRoot : TestVisual, IRenderRoot
{
private NameScope _nameScope = new NameScope();
event EventHandler<NameScopeEventArgs> INameScope.Registered
{
add { _nameScope.Registered += value; }
remove { _nameScope.Registered -= value; }
}
public event EventHandler<NameScopeEventArgs> Unregistered
{
add { _nameScope.Unregistered += value; }
remove { _nameScope.Unregistered -= value; }
}
public IRenderTarget RenderTarget
{
get { throw new NotImplementedException(); }
@ -37,20 +23,5 @@ namespace Perspex.SceneGraph.UnitTests
{
throw new NotImplementedException();
}
public void Register(string name, object element)
{
_nameScope.Register(name, element);
}
public object Find(string name)
{
return _nameScope.Find(name);
}
public void Unregister(string name)
{
_nameScope.Unregister(name);
}
}
}

50
tests/Perspex.SceneGraph.UnitTests/VisualTests.cs

@ -121,55 +121,5 @@ namespace Perspex.SceneGraph.UnitTests
Assert.True(called1);
Assert.True(called2);
}
[Fact]
public void Controls_Should_Add_Themselves_To_Root_NameScope()
{
var child2 = new TestVisual { Name = "bar" };
var child1 = new TestVisual { Name = "foo", Child = child2 };
var root = new TestRoot { Child = child1 };
Assert.Same(child1, root.Find("foo"));
Assert.Same(child2, root.Find("bar"));
}
[Fact]
public void Controls_Should_Add_Themselves_To_NameScopes_In_Attached_Property()
{
var child2 = new TestVisual { Name = "bar", [NameScope.NameScopeProperty] = new NameScope() };
var child1 = new TestVisual { Name = "foo", Child = child2};
var root = new TestRoot { Child = child1 };
Assert.Same(child1, root.Find("foo"));
Assert.Null(root.Find("bar"));
Assert.Same(child2, NameScope.GetNameScope(child2).Find("bar"));
}
[Fact]
public void Controls_Should_Remove_Themselves_From_Root_NameScope()
{
var child2 = new TestVisual { Name = "bar" };
var child1 = new TestVisual { Name = "foo", Child = child2 };
var root = new TestRoot { Child = child1 };
root.Child = null;
Assert.Null(root.Find("foo"));
Assert.Null(root.Find("bar"));
}
[Fact]
public void Controls_Should_Remove_Themselves_From_NameScopes_In_Attached_Property()
{
var child2 = new TestVisual { Name = "bar" };
var child1 = new TestVisual { Name = "foo", Child = child2,[NameScope.NameScopeProperty] = new NameScope() };
var root = new TestRoot { Child = child1 };
root.Child = null;
Assert.Null(root.Find("foo"));
Assert.Null(root.Find("bar"));
Assert.Null(NameScope.GetNameScope(child1).Find("bar"));
}
}
}

Loading…
Cancel
Save