diff --git a/Perspex.Controls/Control.cs b/Perspex.Controls/Control.cs index 24c025f8b5..790021156a 100644 --- a/Perspex.Controls/Control.cs +++ b/Perspex.Controls/Control.cs @@ -7,11 +7,13 @@ namespace Perspex.Controls { using System; + using System.Linq; using System.Reactive.Linq; using Perspex.Collections; using Perspex.Controls.Primitives; using Perspex.Input; using Perspex.Interactivity; + using Perspex.LogicalTree; using Perspex.Rendering; using Perspex.Styling; using Splat; @@ -29,7 +31,7 @@ namespace Perspex.Controls /// - Implements to allow styling to work on the control. /// - Implements to form part of a logical tree. /// - public class Control : InputElement, IControl + public class Control : InputElement, IControl, ISetLogicalParent { /// /// Defines the property. @@ -231,7 +233,6 @@ namespace Perspex.Controls public IControl Parent { get { return this.GetValue(ParentProperty); } - internal set { this.SetValue(ParentProperty, value); } } /// @@ -306,6 +307,55 @@ namespace Perspex.Controls this.RaiseEvent(ev); } + /// + /// Sets the control's logical parent. + /// + /// The parent. + void ISetLogicalParent.SetParent(IControl parent) + { + var old = this.Parent; + + if (old != null && parent != null) + { + throw new InvalidOperationException("The Control already has a parent."); + } + + this.SetValue(ParentProperty, parent); + + if (parent != null) + { + var nameScope = parent.GetSelfAndLogicalAncestors() + .OfType() + .FirstOrDefault(); + + if (nameScope != null) + { + this.RegisterName(nameScope); + + foreach (var descendent in this.GetLogicalDescendents().OfType()) + { + descendent.RegisterName(nameScope); + } + } + } + else if (old != null) + { + var nameScope = old.GetSelfAndLogicalAncestors() + .OfType() + .FirstOrDefault(); + + if (nameScope != null) + { + this.UnregisterName(nameScope); + + foreach (var descendent in this.GetLogicalDescendents().OfType()) + { + descendent.UnregisterName(nameScope); + } + } + } + } + [Obsolete("Obsolete this: use properties instead")] protected static void PseudoClass(PerspexProperty property, string className) { @@ -393,5 +443,29 @@ namespace Perspex.Controls IStyler styler = Locator.Current.GetService(); styler.ApplyStyles(this); } + + /// + /// Registers the control with the specified name scope. + /// + /// The name scope. + private void RegisterName(INameScope nameScope) + { + if (!string.IsNullOrWhiteSpace(this.Name)) + { + nameScope.RegisterName(this.Name, this); + } + } + + /// + /// Unregisters the control with the specified name scope. + /// + /// The name scope. + private void UnregisterName(INameScope nameScope) + { + if (!string.IsNullOrWhiteSpace(this.Name)) + { + nameScope.UnregisterName(this.Name); + } + } } } diff --git a/Perspex.Controls/Decorator.cs b/Perspex.Controls/Decorator.cs index 4ca080be55..bccf348d0e 100644 --- a/Perspex.Controls/Decorator.cs +++ b/Perspex.Controls/Decorator.cs @@ -102,14 +102,14 @@ namespace Perspex.Controls if (oldChild != null) { - oldChild.Parent = null; + ((ISetLogicalParent)oldChild).SetParent(null); this.RemoveVisualChild(oldChild); } if (newChild != null) { this.AddVisualChild(newChild); - newChild.Parent = this; + ((ISetLogicalParent)newChild).SetParent(this); } this.logicalChild.SingleItem = newChild; diff --git a/Perspex.Controls/DropDown.cs b/Perspex.Controls/DropDown.cs index ee1a03f1a3..6897088982 100644 --- a/Perspex.Controls/DropDown.cs +++ b/Perspex.Controls/DropDown.cs @@ -76,7 +76,6 @@ namespace Perspex.Controls protected override void OnTemplateApplied() { var container = this.GetTemplateChild("container"); - ((IItemsPanel)container).ChildLogicalParent = this; this.logicalChildren.Source = ((ILogical)container).LogicalChildren; } @@ -87,12 +86,12 @@ namespace Perspex.Controls if (control1 != null) { - control1.Parent = null; + ((ISetLogicalParent)control1).SetParent(null); } if (control2 != null) { - control2.Parent = this; + ((ISetLogicalParent)control2).SetParent(this); } } } diff --git a/Perspex.Controls/INameScope.cs b/Perspex.Controls/INameScope.cs new file mode 100644 index 0000000000..a7eeb07781 --- /dev/null +++ b/Perspex.Controls/INameScope.cs @@ -0,0 +1,40 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2015 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Controls +{ + /// + /// Defines the interface for object which define a name scope. + /// + public interface INameScope + { + /// + /// Returns an object tha thas the requested name. + /// + /// The name of the object. + /// The named object or null if the named object was not found. + object FindName(string name); + + /// + /// Registers an object with the specified name in the name scope. + /// + /// The name of the object. + /// The object. + /// + /// An object with the same name has already been registered. + /// + void RegisterName(string name, object o); + + /// + /// Unregisters the specified name in the name scope. + /// + /// The name of the object. + /// + /// The name does not exist in the name scope. + /// + void UnregisterName(string name); + } +} \ No newline at end of file diff --git a/Perspex.Controls/ISetLogicalParent.cs b/Perspex.Controls/ISetLogicalParent.cs new file mode 100644 index 0000000000..4c91aa6bdd --- /dev/null +++ b/Perspex.Controls/ISetLogicalParent.cs @@ -0,0 +1,23 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2015 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Controls +{ + /// + /// Defines an interface through which a 's logical parent can be set. + /// + /// + /// You should not usually need to use this interface - it is for advanced scenarios only. + /// + public interface ISetLogicalParent + { + /// + /// Sets the control's parent. + /// + /// The parent. + void SetParent(IControl parent); + } +} \ No newline at end of file diff --git a/Perspex.Controls/Panel.cs b/Perspex.Controls/Panel.cs index 7496d3fd6f..26d8825ec4 100644 --- a/Perspex.Controls/Panel.cs +++ b/Perspex.Controls/Panel.cs @@ -122,7 +122,7 @@ namespace Perspex.Controls { foreach (var control in controls) { - control.Parent = null; + ((ISetLogicalParent)control).SetParent(null); } } @@ -132,9 +132,11 @@ namespace Perspex.Controls /// The controls. private void SetLogicalParent(IEnumerable controls) { + var parent = this.childLogicalParent as Control; + foreach (var control in controls) { - control.Parent = this.childLogicalParent as Control; + ((ISetLogicalParent)control).SetParent(parent); } } diff --git a/Perspex.Controls/Perspex.Controls.csproj b/Perspex.Controls/Perspex.Controls.csproj index cc98e7f0e4..328b44d18f 100644 --- a/Perspex.Controls/Perspex.Controls.csproj +++ b/Perspex.Controls/Perspex.Controls.csproj @@ -37,6 +37,8 @@ + + diff --git a/Perspex.Controls/Popup.cs b/Perspex.Controls/Popup.cs index 746ef04278..41cdc126b9 100644 --- a/Perspex.Controls/Popup.cs +++ b/Perspex.Controls/Popup.cs @@ -185,7 +185,6 @@ namespace Perspex.Controls { this.popupRoot = new PopupRoot(this.DependencyResolver) { - Parent = this, [~PopupRoot.ContentProperty] = this[~ChildProperty], [~PopupRoot.WidthProperty] = this[~WidthProperty], [~PopupRoot.HeightProperty] = this[~HeightProperty], @@ -194,6 +193,8 @@ namespace Perspex.Controls [~PopupRoot.MinHeightProperty] = this[~MinHeightProperty], [~PopupRoot.MaxHeightProperty] = this[~MaxHeightProperty], }; + + ((ISetLogicalParent)this.popupRoot).SetParent(this); } this.popupRoot.SetPosition(this.GetPosition()); @@ -279,12 +280,12 @@ namespace Perspex.Controls { if (values.Item1 != null) { - values.Item1.Parent = null; + ((ISetLogicalParent)values.Item1).SetParent(null); } if (values.Item2 != null) { - values.Item2.Parent = this; + ((ISetLogicalParent)values.Item2).SetParent(this); } this.logicalChild.SingleItem = values.Item2; diff --git a/Perspex.Controls/Presenters/ContentPresenter.cs b/Perspex.Controls/Presenters/ContentPresenter.cs index 5556be8a98..aaf110c0eb 100644 --- a/Perspex.Controls/Presenters/ContentPresenter.cs +++ b/Perspex.Controls/Presenters/ContentPresenter.cs @@ -87,7 +87,11 @@ namespace Perspex.Controls.Presenters if (content != null) { result = this.MaterializeDataTemplate(content); - result.Parent = this.TemplatedParent as Control; + + if (result.Parent == null) + { + ((ISetLogicalParent)result).SetParent(this.TemplatedParent as Control); + } var templatedParent = this.TemplatedParent as TemplatedControl; diff --git a/Perspex.Controls/Primitives/AdornerDecorator.cs b/Perspex.Controls/Primitives/AdornerDecorator.cs index 4fb129063c..b877118f5d 100644 --- a/Perspex.Controls/Primitives/AdornerDecorator.cs +++ b/Perspex.Controls/Primitives/AdornerDecorator.cs @@ -11,7 +11,7 @@ namespace Perspex.Controls.Primitives public AdornerDecorator() { this.AdornerLayer = new AdornerLayer(); - this.AdornerLayer.Parent = this; + ((ISetLogicalParent)this.AdornerLayer).SetParent(this); this.AdornerLayer.ZIndex = int.MaxValue; this.AddVisualChild(this.AdornerLayer); } diff --git a/Perspex.Controls/Primitives/TemplatedControl.cs b/Perspex.Controls/Primitives/TemplatedControl.cs index 0cf46790c9..6e7fe0b822 100644 --- a/Perspex.Controls/Primitives/TemplatedControl.cs +++ b/Perspex.Controls/Primitives/TemplatedControl.cs @@ -140,7 +140,7 @@ namespace Perspex.Controls.Primitives var child = this.Template.Build(this); this.SetTemplatedParent(child); this.AddVisualChild(child); - child.Parent = this; + ((ISetLogicalParent)child).SetParent(this); foreach (var i in this.GetTemplateChildren()) { diff --git a/Perspex.Controls/ToolTip.cs b/Perspex.Controls/ToolTip.cs index 91fd3c6d82..c9a6753327 100644 --- a/Perspex.Controls/ToolTip.cs +++ b/Perspex.Controls/ToolTip.cs @@ -117,7 +117,7 @@ namespace Perspex.Controls var cp = MouseDevice.Instance.GetPosition(control); var position = control.PointToScreen(cp) + new Vector(0, 22); - popup.Parent = control; + ((ISetLogicalParent)popup).SetParent(control); ((ToolTip)popup.Content).Content = GetTip(control); popup.SetPosition(position); popup.Show();