diff --git a/src/Perspex.Controls/Button.cs b/src/Perspex.Controls/Button.cs
index 29eb4c3628..a1ca0e11a6 100644
--- a/src/Perspex.Controls/Button.cs
+++ b/src/Perspex.Controls/Button.cs
@@ -147,13 +147,13 @@ namespace Perspex.Controls
}
///
- protected override void OnAttachedToVisualTree(IRenderRoot root)
+ protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
- base.OnAttachedToVisualTree(root);
+ base.OnAttachedToVisualTree(e);
if (IsDefault)
{
- var inputElement = root as IInputElement;
+ var inputElement = e.Root as IInputElement;
if (inputElement != null)
{
@@ -198,13 +198,13 @@ namespace Perspex.Controls
}
///
- protected override void OnDetachedFromVisualTree(IRenderRoot oldRoot)
+ protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
- base.OnDetachedFromVisualTree(oldRoot);
+ base.OnDetachedFromVisualTree(e);
if (IsDefault)
{
- var inputElement = oldRoot as IInputElement;
+ var inputElement = e.Root as IInputElement;
if (inputElement != null)
{
diff --git a/src/Perspex.Controls/Carousel.cs b/src/Perspex.Controls/Carousel.cs
index 1c12b634d0..97745e1452 100644
--- a/src/Perspex.Controls/Carousel.cs
+++ b/src/Perspex.Controls/Carousel.cs
@@ -55,11 +55,5 @@ namespace Perspex.Controls
{
// Ignore pointer presses.
}
-
- ///
- protected override void OnTemplateApplied()
- {
- base.OnTemplateApplied();
- }
}
}
diff --git a/src/Perspex.Controls/ContentControl.cs b/src/Perspex.Controls/ContentControl.cs
index 1640853391..c4c1c94e31 100644
--- a/src/Perspex.Controls/ContentControl.cs
+++ b/src/Perspex.Controls/ContentControl.cs
@@ -104,12 +104,12 @@ namespace Perspex.Controls
}
///
- protected override void OnTemplateApplied()
+ protected override void OnTemplateApplied(INameScope nameScope)
{
// We allow ContentControls without ContentPresenters in the template. This can be
// useful for e.g. a simple ToggleButton that displays an image. There's no need to
// have a ContentPresenter in the visual tree for that.
- Presenter = this.FindTemplateChild("PART_ContentPresenter");
+ Presenter = nameScope.Find("PART_ContentPresenter");
}
}
}
diff --git a/src/Perspex.Controls/Control.cs b/src/Perspex.Controls/Control.cs
index c4d85e7908..b382e82675 100644
--- a/src/Perspex.Controls/Control.cs
+++ b/src/Perspex.Controls/Control.cs
@@ -19,7 +19,6 @@ namespace Perspex.Controls
///
/// The control class extends and adds the following features:
///
- /// - A .
/// - An inherited .
/// - A property to allow user-defined data to be attached to the control.
/// - A collection of class strings for custom styling.
@@ -71,7 +70,6 @@ namespace Perspex.Controls
private readonly Classes _classes = new Classes();
private DataTemplates _dataTemplates;
private IControl _focusAdorner;
- private string _name;
private IPerspexList _logicalChildren;
private Styles _styles;
@@ -165,36 +163,6 @@ namespace Perspex.Controls
}
}
- ///
- /// Gets or sets the name of the control.
- ///
- ///
- /// A control's name is used to uniquely identify a control within the control's name
- /// scope. Once a control is added to a visual tree, its name cannot be changed.
- ///
- public string Name
- {
- get
- {
- return _name;
- }
-
- set
- {
- if (_name != null)
- {
- throw new InvalidOperationException("Name already set.");
- }
-
- if (((IVisual)this).VisualParent != null)
- {
- throw new InvalidOperationException("Cannot set Name : control already added to tree.");
- }
-
- _name = value;
- }
- }
-
///
/// Gets or sets the styles for the control.
///
@@ -404,9 +372,9 @@ namespace Perspex.Controls
}
///
- protected override void OnAttachedToVisualTree(IRenderRoot root)
+ protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
- base.OnAttachedToVisualTree(root);
+ base.OnAttachedToVisualTree(e);
IStyler styler = PerspexLocator.Current.GetService();
diff --git a/src/Perspex.Controls/DropDown.cs b/src/Perspex.Controls/DropDown.cs
index 4af34d689d..31a44ad7d0 100644
--- a/src/Perspex.Controls/DropDown.cs
+++ b/src/Perspex.Controls/DropDown.cs
@@ -125,14 +125,14 @@ namespace Perspex.Controls
base.OnPointerPressed(e);
}
- protected override void OnTemplateApplied()
+ protected override void OnTemplateApplied(INameScope nameScope)
{
if (_popup != null)
{
_popup.Opened -= PopupOpened;
}
- _popup = this.GetTemplateChild("PART_Popup");
+ _popup = nameScope.Get("PART_Popup");
_popup.Opened += PopupOpened;
}
diff --git a/src/Perspex.Controls/GridSplitter.cs b/src/Perspex.Controls/GridSplitter.cs
index 1fc78549eb..9a0ef0b988 100644
--- a/src/Perspex.Controls/GridSplitter.cs
+++ b/src/Perspex.Controls/GridSplitter.cs
@@ -34,9 +34,9 @@ namespace Perspex.Controls
}
}
- protected override void OnAttachedToVisualTree(IRenderRoot root)
+ protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
- base.OnAttachedToVisualTree(root);
+ base.OnAttachedToVisualTree(e);
_grid = this.GetVisualParent();
}
}
diff --git a/src/Perspex.Controls/ItemsControl.cs b/src/Perspex.Controls/ItemsControl.cs
index 9da2ae45ba..5c06dbc04e 100644
--- a/src/Perspex.Controls/ItemsControl.cs
+++ b/src/Perspex.Controls/ItemsControl.cs
@@ -147,9 +147,9 @@ namespace Perspex.Controls
}
///
- protected override void OnTemplateApplied()
+ protected override void OnTemplateApplied(INameScope nameScope)
{
- Presenter = this.FindTemplateChild("PART_ItemsPresenter");
+ Presenter = nameScope.Find("PART_ItemsPresenter");
}
///
diff --git a/src/Perspex.Controls/Menu.cs b/src/Perspex.Controls/Menu.cs
index 3513843c37..9e9ee451e8 100644
--- a/src/Perspex.Controls/Menu.cs
+++ b/src/Perspex.Controls/Menu.cs
@@ -96,15 +96,12 @@ namespace Perspex.Controls
IsOpen = true;
}
- ///
- /// Called when the is attached to the visual tree.
- ///
- /// The root of the visual tree.
- protected override void OnAttachedToVisualTree(IRenderRoot root)
+ ///
+ protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
- base.OnAttachedToVisualTree(root);
+ base.OnAttachedToVisualTree(e);
- var topLevel = root as TopLevel;
+ var topLevel = e.Root as TopLevel;
topLevel.Deactivated += Deactivated;
@@ -117,7 +114,7 @@ namespace Perspex.Controls
pointerPress,
Disposable.Create(() => topLevel.Deactivated -= Deactivated));
- var inputRoot = root as IInputRoot;
+ var inputRoot = e.Root as IInputRoot;
if (inputRoot != null && inputRoot.AccessKeyHandler != null)
{
@@ -125,13 +122,10 @@ namespace Perspex.Controls
}
}
- ///
- /// Called when the is detached from the visual tree.
- ///
- /// The root of the visual tree being detached from.
- protected override void OnDetachedFromVisualTree(IRenderRoot oldRoot)
+ ///
+ protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
- base.OnDetachedFromVisualTree(oldRoot);
+ base.OnDetachedFromVisualTree(e);
_subscription.Dispose();
}
diff --git a/src/Perspex.Controls/MenuItem.cs b/src/Perspex.Controls/MenuItem.cs
index dde7dc16b3..4d73240604 100644
--- a/src/Perspex.Controls/MenuItem.cs
+++ b/src/Perspex.Controls/MenuItem.cs
@@ -376,11 +376,11 @@ namespace Perspex.Controls
///
/// Called when the MenuItem's template has been applied.
///
- protected override void OnTemplateApplied()
+ protected override void OnTemplateApplied(INameScope nameScope)
{
- base.OnTemplateApplied();
+ base.OnTemplateApplied(nameScope);
- _popup = this.GetTemplateChild("PART_Popup");
+ _popup = nameScope.Get("PART_Popup");
_popup.DependencyResolver = DependencyResolver.Instance;
_popup.PopupRootCreated += PopupRootCreated;
_popup.Opened += PopupOpened;
diff --git a/src/Perspex.Controls/Primitives/AccessText.cs b/src/Perspex.Controls/Primitives/AccessText.cs
index 0168974248..9ad2640534 100644
--- a/src/Perspex.Controls/Primitives/AccessText.cs
+++ b/src/Perspex.Controls/Primitives/AccessText.cs
@@ -108,14 +108,11 @@ namespace Perspex.Controls.Primitives
return result.WithHeight(result.Height + 1);
}
- ///
- /// Called when the control is attached to a visual tree.
- ///
- /// The root of the visual tree.
- protected override void OnAttachedToVisualTree(IRenderRoot root)
+ ///
+ protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
- base.OnAttachedToVisualTree(root);
- _accessKeys = (root as IInputRoot)?.AccessKeyHandler;
+ base.OnAttachedToVisualTree(e);
+ _accessKeys = (e.Root as IInputRoot)?.AccessKeyHandler;
if (_accessKeys != null && AccessKey != 0)
{
@@ -123,13 +120,10 @@ namespace Perspex.Controls.Primitives
}
}
- ///
- /// Called when the control is detached from a visual tree.
- ///
- /// The root of the visual tree.
- protected override void OnDetachedFromVisualTree(IRenderRoot root)
+ ///
+ protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
- base.OnDetachedFromVisualTree(root);
+ base.OnDetachedFromVisualTree(e);
if (_accessKeys != null && AccessKey != 0)
{
diff --git a/src/Perspex.Controls/Primitives/Popup.cs b/src/Perspex.Controls/Primitives/Popup.cs
index 6396b29843..138c5b586f 100644
--- a/src/Perspex.Controls/Primitives/Popup.cs
+++ b/src/Perspex.Controls/Primitives/Popup.cs
@@ -212,23 +212,17 @@ namespace Perspex.Controls.Primitives
return new Size();
}
- ///
- /// Called when the control is added to the visual tree.
- ///
- /// THe root of the visual tree.
- protected override void OnAttachedToVisualTree(IRenderRoot root)
+ ///
+ protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
- base.OnAttachedToVisualTree(root);
- _topLevel = root as TopLevel;
+ base.OnAttachedToVisualTree(e);
+ _topLevel = e.Root as TopLevel;
}
- ///
- /// Called when the control is removed to the visual tree.
- ///
- /// THe root of the visual tree.
- protected override void OnDetachedFromVisualTree(IRenderRoot root)
+ ///
+ protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
- base.OnDetachedFromVisualTree(root);
+ base.OnDetachedFromVisualTree(e);
_topLevel = null;
}
diff --git a/src/Perspex.Controls/Primitives/PopupRoot.cs b/src/Perspex.Controls/Primitives/PopupRoot.cs
index 4e628f79f0..aeaa63dd0f 100644
--- a/src/Perspex.Controls/Primitives/PopupRoot.cs
+++ b/src/Perspex.Controls/Primitives/PopupRoot.cs
@@ -93,9 +93,9 @@ namespace Perspex.Controls.Primitives
}
///
- protected override void OnTemplateApplied()
+ protected override void OnTemplateApplied(INameScope nameScope)
{
- base.OnTemplateApplied();
+ base.OnTemplateApplied(nameScope);
if (Parent.TemplatedParent != null)
{
diff --git a/src/Perspex.Controls/Primitives/TemplatedControl.cs b/src/Perspex.Controls/Primitives/TemplatedControl.cs
index bbe9836e03..868da9b670 100644
--- a/src/Perspex.Controls/Primitives/TemplatedControl.cs
+++ b/src/Perspex.Controls/Primitives/TemplatedControl.cs
@@ -195,6 +195,8 @@ namespace Perspex.Controls.Primitives
_templateLog.Verbose("Creating control template");
var child = Template.Build(this);
+ var nameScope = new NameScope();
+ NameScope.SetNameScope((Visual)child, nameScope);
// We need to call SetTemplatedParentAndApplyChildTemplates twice - once
// before the controls are added to the visual tree so that the logical
@@ -207,7 +209,7 @@ namespace Perspex.Controls.Primitives
AddVisualChild((Visual)child);
SetTemplatedParentAndApplyChildTemplates(child);
- OnTemplateApplied();
+ OnTemplateApplied(nameScope);
}
_templateApplied = true;
@@ -217,7 +219,8 @@ namespace Perspex.Controls.Primitives
///
/// Called when the control's template is applied.
///
- protected virtual void OnTemplateApplied()
+ /// The template name scope.
+ protected virtual void OnTemplateApplied(INameScope nameScope)
{
}
diff --git a/src/Perspex.Controls/ProgressBar.cs b/src/Perspex.Controls/ProgressBar.cs
index f9a8d867d0..4bc1fdb9d2 100644
--- a/src/Perspex.Controls/ProgressBar.cs
+++ b/src/Perspex.Controls/ProgressBar.cs
@@ -26,9 +26,9 @@ namespace Perspex.Controls
}
///
- protected override void OnTemplateApplied()
+ protected override void OnTemplateApplied(INameScope nameScope)
{
- _indicator = this.GetTemplateChild("PART_Indicator");
+ _indicator = nameScope.Get("PART_Indicator");
UpdateIndicator(Bounds.Size);
}
diff --git a/src/Perspex.Controls/Templates/TemplateExtensions.cs b/src/Perspex.Controls/Templates/TemplateExtensions.cs
index e4e61c722f..b8cd9e25c2 100644
--- a/src/Perspex.Controls/Templates/TemplateExtensions.cs
+++ b/src/Perspex.Controls/Templates/TemplateExtensions.cs
@@ -41,27 +41,6 @@ namespace Perspex.Controls.Templates
return null;
}
- public static T FindTemplateChild(this ITemplatedControl control, string name) where T : INamed
- {
- return control.GetTemplateChildren().OfType().SingleOrDefault(x => x.Name == name);
- }
-
- public static T GetTemplateChild(this ITemplatedControl control, string name) where T : INamed
- {
- var result = control.FindTemplateChild(name);
-
- if (result == null)
- {
- throw new InvalidOperationException(string.Format(
- "Could not find template child '{0}' of type '{1}' in template for '{2}'.",
- name,
- typeof(T).FullName,
- control.GetType().FullName));
- }
-
- return result;
- }
-
public static IEnumerable GetTemplateChildren(this ITemplatedControl control)
{
var visual = control as IVisual;
diff --git a/src/Perspex.Controls/TextBox.cs b/src/Perspex.Controls/TextBox.cs
index affa38c023..36fd8c0cb9 100644
--- a/src/Perspex.Controls/TextBox.cs
+++ b/src/Perspex.Controls/TextBox.cs
@@ -136,9 +136,9 @@ namespace Perspex.Controls
set { SetValue(TextWrappingProperty, value); }
}
- protected override void OnTemplateApplied()
+ protected override void OnTemplateApplied(INameScope nameScope)
{
- _presenter = this.GetTemplateChild("PART_TextPresenter");
+ _presenter = nameScope.Get("PART_TextPresenter");
_presenter.Cursor = new Cursor(StandardCursorType.Ibeam);
}
diff --git a/src/Perspex.Controls/TopLevel.cs b/src/Perspex.Controls/TopLevel.cs
index 21247721bd..6aacac85ff 100644
--- a/src/Perspex.Controls/TopLevel.cs
+++ b/src/Perspex.Controls/TopLevel.cs
@@ -287,9 +287,9 @@ namespace Perspex.Controls
}
///
- protected override void OnAttachedToVisualTree(IRenderRoot root)
+ protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
- base.OnAttachedToVisualTree(root);
+ base.OnAttachedToVisualTree(e);
throw new InvalidOperationException(
$"Control '{GetType().Name}' is a top level control and cannot be added as a child.");
diff --git a/src/Perspex.Controls/TreeViewItem.cs b/src/Perspex.Controls/TreeViewItem.cs
index bd499e4f10..da263ca951 100644
--- a/src/Perspex.Controls/TreeViewItem.cs
+++ b/src/Perspex.Controls/TreeViewItem.cs
@@ -83,9 +83,9 @@ namespace Perspex.Controls
}
///
- protected override void OnAttachedToVisualTree(IRenderRoot root)
+ protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
- base.OnAttachedToVisualTree(root);
+ base.OnAttachedToVisualTree(e);
_treeView = this.GetVisualAncestors().OfType().FirstOrDefault();
}
diff --git a/src/Perspex.Input/InputElement.cs b/src/Perspex.Input/InputElement.cs
index 7e0907c9e8..5be37be76a 100644
--- a/src/Perspex.Input/InputElement.cs
+++ b/src/Perspex.Input/InputElement.cs
@@ -348,9 +348,9 @@ namespace Perspex.Input
}
///
- protected override void OnDetachedFromVisualTree(IRenderRoot oldRoot)
+ protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
- base.OnDetachedFromVisualTree(oldRoot);
+ base.OnDetachedFromVisualTree(e);
if (IsFocused)
{
@@ -359,9 +359,9 @@ namespace Perspex.Input
}
///
- protected override void OnAttachedToVisualTree(IRenderRoot root)
+ protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
- base.OnAttachedToVisualTree(root);
+ base.OnAttachedToVisualTree(e);
UpdateIsEnabledCore();
}
diff --git a/src/Perspex.SceneGraph/INameScope.cs b/src/Perspex.SceneGraph/INameScope.cs
new file mode 100644
index 0000000000..13b197f1ad
--- /dev/null
+++ b/src/Perspex.SceneGraph/INameScope.cs
@@ -0,0 +1,31 @@
+// 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.
+
+namespace Perspex
+{
+ ///
+ /// Defines a name scope.
+ ///
+ public interface INameScope
+ {
+ ///
+ /// Registers an element eith the name scope.
+ ///
+ /// The element name.
+ /// The element.
+ void Register(string name, object element);
+
+ ///
+ /// Finds a named element in the name scope.
+ ///
+ /// The name.
+ /// The element, or null if the name was not found.
+ object Find(string name);
+
+ ///
+ /// Unregisters an element with the name scope.
+ ///
+ /// The name.
+ void Unregister(string name);
+ }
+}
diff --git a/src/Perspex.Styling/Styling/INamed.cs b/src/Perspex.SceneGraph/INamed.cs
similarity index 58%
rename from src/Perspex.Styling/Styling/INamed.cs
rename to src/Perspex.SceneGraph/INamed.cs
index bd5b806d7e..5ceda5a265 100644
--- a/src/Perspex.Styling/Styling/INamed.cs
+++ b/src/Perspex.SceneGraph/INamed.cs
@@ -1,10 +1,16 @@
// 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.
-namespace Perspex.Styling
+namespace Perspex
{
+ ///
+ /// Interface for named elements.
+ ///
public interface INamed
{
+ ///
+ /// Gets the element name.
+ ///
string Name { get; }
}
}
diff --git a/src/Perspex.SceneGraph/NameScope.cs b/src/Perspex.SceneGraph/NameScope.cs
new file mode 100644
index 0000000000..403a51bf1f
--- /dev/null
+++ b/src/Perspex.SceneGraph/NameScope.cs
@@ -0,0 +1,80 @@
+// 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 System.Collections.Generic;
+
+namespace Perspex
+{
+ ///
+ /// Implements a name scope.
+ ///
+ public class NameScope : INameScope
+ {
+ ///
+ /// Defines the NameScope attached property.
+ ///
+ public static readonly PerspexProperty NameScopeProperty =
+ PerspexProperty.RegisterAttached("NameScope");
+
+ private Dictionary _inner = new Dictionary();
+
+ ///
+ /// Gets the value of the attached on a visual.
+ ///
+ /// The visual.
+ /// The value of the NameScope attached property.
+ public static INameScope GetNameScope(Visual visual)
+ {
+ return visual.GetValue(NameScopeProperty);
+ }
+
+ ///
+ /// Sets the value of the attached on a visual.
+ ///
+ /// The visual.
+ /// The value to set.
+ public static void SetNameScope(Visual visual, INameScope value)
+ {
+ visual.SetValue(NameScopeProperty, value);
+ }
+
+ ///
+ /// Registers an element with the name scope.
+ ///
+ /// The element name.
+ /// The element.
+ public void Register(string name, object element)
+ {
+ Contract.Requires(name != null);
+ Contract.Requires(element != null);
+
+ _inner.Add(name, element);
+ }
+
+ ///
+ /// Finds a named element in the name scope.
+ ///
+ /// The name.
+ /// The element, or null if the name was not found.
+ public object Find(string name)
+ {
+ Contract.Requires(name != null);
+
+ object result;
+ _inner.TryGetValue(name, out result);
+ return result;
+ }
+
+ ///
+ /// Unregisters an element with the name scope.
+ ///
+ /// The name.
+ public void Unregister(string name)
+ {
+ Contract.Requires(name != null);
+
+ _inner.Remove(name);
+ }
+ }
+}
diff --git a/src/Perspex.SceneGraph/NameScopeExtensions.cs b/src/Perspex.SceneGraph/NameScopeExtensions.cs
new file mode 100644
index 0000000000..3e80f8e838
--- /dev/null
+++ b/src/Perspex.SceneGraph/NameScopeExtensions.cs
@@ -0,0 +1,68 @@
+// 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 System.Collections.Generic;
+
+namespace Perspex
+{
+ ///
+ /// Extension methods for .
+ ///
+ public static class NameScopeExtensions
+ {
+ ///
+ /// Finds a named element in an .
+ ///
+ /// The element type.
+ /// The name scope.
+ /// The name.
+ /// The named element or null if not found.
+ public static T Find(this INameScope nameScope, string name)
+ where T : class
+ {
+ Contract.Requires(nameScope != null);
+ Contract.Requires(name != null);
+
+ var result = nameScope.Find(name);
+
+ if (result != null && !(result is T))
+ {
+ throw new InvalidOperationException(
+ $"Expected control '{name}' to be '{typeof(T)} but it was '{result.GetType()}'.");
+ }
+
+ return (T)result;
+ }
+
+ ///
+ /// Gets a named element from an or throws if no element of the
+ /// requested name was found.
+ ///
+ /// The element type.
+ /// The name scope.
+ /// The name.
+ /// The named element.
+ public static T Get(this INameScope nameScope, string name)
+ where T : class
+ {
+ Contract.Requires(nameScope != null);
+ Contract.Requires(name != null);
+
+ var result = nameScope.Find(name);
+
+ if (result == null)
+ {
+ throw new KeyNotFoundException($"Could not find control '{name}'.");
+ }
+
+ if (!(result is T))
+ {
+ throw new InvalidOperationException(
+ $"Expected control '{name}' to be '{typeof(T)} but it was '{result.GetType()}'.");
+ }
+
+ return (T)result;
+ }
+ }
+}
diff --git a/src/Perspex.SceneGraph/Perspex.SceneGraph.csproj b/src/Perspex.SceneGraph/Perspex.SceneGraph.csproj
index d2d897142f..871e14750f 100644
--- a/src/Perspex.SceneGraph/Perspex.SceneGraph.csproj
+++ b/src/Perspex.SceneGraph/Perspex.SceneGraph.csproj
@@ -55,6 +55,8 @@
+
+
@@ -101,6 +103,8 @@
+
+
@@ -122,6 +126,7 @@
+
diff --git a/src/Perspex.SceneGraph/Visual.cs b/src/Perspex.SceneGraph/Visual.cs
index a63b8b0aad..53c6b8e66d 100644
--- a/src/Perspex.SceneGraph/Visual.cs
+++ b/src/Perspex.SceneGraph/Visual.cs
@@ -26,7 +26,7 @@ namespace Perspex
/// To traverse the scene graph (aka Visual Tree), use the extension methods defined
/// in .
///
- public class Visual : Animatable, IVisual
+ public class Visual : Animatable, IVisual, INamed
{
///
/// Defines the property.
@@ -76,6 +76,11 @@ namespace Perspex
public static readonly PerspexProperty ZIndexProperty =
PerspexProperty.Register(nameof(ZIndex));
+ ///
+ /// The name of the visual, if any.
+ ///
+ private string _name;
+
///
/// Holds the children of the visual.
///
@@ -128,6 +133,16 @@ namespace Perspex
_visualChildren.CollectionChanged += VisualChildrenChanged;
}
+ ///
+ /// Raised when the control is attached to a rooted visual tree.
+ ///
+ public event EventHandler AttachedToVisualTree;
+
+ ///
+ /// Raised when the control is detached from a rooted visual tree.
+ ///
+ public event EventHandler DetachedFromVisualTree;
+
///
/// Gets the bounds of the scene graph node relative to its parent.
///
@@ -163,6 +178,36 @@ namespace Perspex
set { SetValue(IsVisibleProperty, value); }
}
+ ///
+ /// Gets or sets the name of the visual.
+ ///
+ ///
+ /// An element's name is used to uniquely identify a control within the control's name
+ /// scope. Once the element is added to a visual tree, its name cannot be changed.
+ ///
+ public string Name
+ {
+ get
+ {
+ return _name;
+ }
+
+ set
+ {
+ if (value.Trim() == string.Empty)
+ {
+ throw new InvalidOperationException("Cannot set Name to empty string.");
+ }
+
+ if (_isAttachedToVisualTree)
+ {
+ throw new InvalidOperationException("Cannot set Name : control already added to tree.");
+ }
+
+ _name = value;
+ }
+ }
+
///
/// Gets the opacity of the scene graph node.
///
@@ -340,17 +385,19 @@ namespace Perspex
///
/// Called when the control is added to a visual tree.
///
- /// The root of the visual tree.
- protected virtual void OnAttachedToVisualTree(IRenderRoot root)
+ /// The event args.
+ protected virtual void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
+ AttachedToVisualTree?.Invoke(this, e);
}
///
/// Called when the control is removed from a visual tree.
///
- /// The root of the visual tree.
- protected virtual void OnDetachedFromVisualTree(IRenderRoot root)
+ /// The event args.
+ protected virtual void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
+ DetachedFromVisualTree?.Invoke(this, e);
}
///
@@ -367,6 +414,60 @@ namespace Perspex
}
}
+ ///
+ /// Gets the event args for an or
+ /// event.
+ ///
+ ///
+ /// A if the visual currently has a root;
+ /// otherwise null.
+ ///
+ 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;
+ }
+
+ ///
+ /// Gets the for this element based on the
+ /// parent's args.
+ ///
+ /// The parent args.
+ /// The args for this element.
+ 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;
+ }
+ }
+
///
/// Gets the root of the controls visual tree and the distance from the root.
///
@@ -436,28 +537,23 @@ namespace Perspex
{
if (_visualParent != value)
{
- var old = _visualParent;
- var oldRoot = this.GetVisualAncestors().OfType().FirstOrDefault();
- var newRoot = default(IRenderRoot);
-
- if (value != null)
- {
- newRoot = value.GetSelfAndVisualAncestors().OfType().FirstOrDefault();
- }
+ var oldArgs = GetAttachmentEventArgs();
_visualParent = value;
- if (oldRoot != null)
+ if (oldArgs != null)
{
- NotifyDetachedFromVisualTree(oldRoot);
+ NotifyDetachedFromVisualTree(oldArgs);
}
- if (newRoot != null)
+ var newArgs = GetAttachmentEventArgs();
+
+ if (newArgs != null)
{
- NotifyAttachedToVisualTree(newRoot);
+ NotifyAttachedToVisualTree(newArgs);
}
- RaisePropertyChanged(VisualParentProperty, old, value, BindingPriority.LocalValue);
+ RaisePropertyChanged(VisualParentProperty, oldArgs, value, BindingPriority.LocalValue);
}
}
@@ -491,43 +587,56 @@ namespace Perspex
}
///
- /// Calls the method for this control
- /// and all of its visual descendents.
+ /// Calls the method
+ /// for this control and all of its visual descendents.
///
- /// The root of the visual tree.
- private void NotifyAttachedToVisualTree(IRenderRoot root)
+ /// The event args.
+ private void NotifyAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
_visualLogger.Verbose("Attached to visual tree");
_isAttachedToVisualTree = true;
- OnAttachedToVisualTree(root);
+
+ if (Name != null && e.NameScope != null)
+ {
+ e.NameScope.Register(Name, this);
+ }
+
+ OnAttachedToVisualTree(e);
if (_visualChildren != null)
{
foreach (Visual child in _visualChildren.OfType())
{
- child.NotifyAttachedToVisualTree(root);
+ var ce = child.GetAttachmentEventArgs(e);
+ child.NotifyAttachedToVisualTree(ce);
}
}
}
///
- /// Calls the method for this control
- /// and all of its visual descendents.
+ /// Calls the method
+ /// for this control and all of its visual descendents.
///
- /// The root of the visual tree.
- private void NotifyDetachedFromVisualTree(IRenderRoot root)
+ /// The event args.
+ private void NotifyDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
_visualLogger.Verbose("Detached from visual tree");
+ if (Name != null && e.NameScope != null)
+ {
+ e.NameScope.Unregister(Name);
+ }
+
_isAttachedToVisualTree = false;
- OnDetachedFromVisualTree(root);
+ OnDetachedFromVisualTree(e);
if (_visualChildren != null)
{
foreach (Visual child in _visualChildren.OfType())
{
- child.NotifyDetachedFromVisualTree(root);
+ var ce = child.GetAttachmentEventArgs(e);
+ child.NotifyDetachedFromVisualTree(ce);
}
}
}
diff --git a/src/Perspex.SceneGraph/VisualTreeAttachmentEventArgs.cs b/src/Perspex.SceneGraph/VisualTreeAttachmentEventArgs.cs
new file mode 100644
index 0000000000..41f37e26fc
--- /dev/null
+++ b/src/Perspex.SceneGraph/VisualTreeAttachmentEventArgs.cs
@@ -0,0 +1,36 @@
+// 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.Rendering;
+
+namespace Perspex
+{
+ ///
+ /// Holds the event arguments for the and
+ /// events.
+ ///
+ public class VisualTreeAttachmentEventArgs : EventArgs
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The root visual.
+ /// The name scope.
+ public VisualTreeAttachmentEventArgs(IRenderRoot root, INameScope nameScope)
+ {
+ Root = root;
+ NameScope = nameScope;
+ }
+
+ ///
+ /// Gets the root of the visual tree that the visual is being attached to or detached from.
+ ///
+ public IRenderRoot Root { get; }
+
+ ///
+ /// Gets the element's name scope.
+ ///
+ public INameScope NameScope { get; }
+ }
+}
diff --git a/src/Perspex.Styling/Perspex.Styling.csproj b/src/Perspex.Styling/Perspex.Styling.csproj
index c0776e27c2..a8e997821e 100644
--- a/src/Perspex.Styling/Perspex.Styling.csproj
+++ b/src/Perspex.Styling/Perspex.Styling.csproj
@@ -46,7 +46,6 @@
-
diff --git a/tests/Perspex.Controls.UnitTests/Primitives/PopupTests.cs b/tests/Perspex.Controls.UnitTests/Primitives/PopupTests.cs
index 28da8d75e3..18d7867b0f 100644
--- a/tests/Perspex.Controls.UnitTests/Primitives/PopupTests.cs
+++ b/tests/Perspex.Controls.UnitTests/Primitives/PopupTests.cs
@@ -214,7 +214,7 @@ namespace Perspex.Controls.UnitTests.Primitives
};
target.ApplyTemplate();
- var popup = target.GetTemplateChild("popup");
+ var popup = (Popup)target.GetTemplateChildren().First(x => x.Name == "popup");
popup.Open();
var popupRoot = popup.PopupRoot;
diff --git a/tests/Perspex.Controls.UnitTests/Primitives/ScrollBarTests.cs b/tests/Perspex.Controls.UnitTests/Primitives/ScrollBarTests.cs
index 6ba3adc899..eb19ac1493 100644
--- a/tests/Perspex.Controls.UnitTests/Primitives/ScrollBarTests.cs
+++ b/tests/Perspex.Controls.UnitTests/Primitives/ScrollBarTests.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
+using System.Linq;
using Perspex.Controls.Primitives;
using Perspex.Controls.Templates;
using Perspex.Media;
@@ -20,7 +21,7 @@ namespace Perspex.Controls.UnitTests.Primitives
};
target.ApplyTemplate();
- var track = target.GetTemplateChild