diff --git a/Perspex.Controls/ContentControl.cs b/Perspex.Controls/ContentControl.cs
index e3211721f0..a740a22b2b 100644
--- a/Perspex.Controls/ContentControl.cs
+++ b/Perspex.Controls/ContentControl.cs
@@ -1,6 +1,6 @@
// -----------------------------------------------------------------------
//
-// Copyright 2014 MIT Licence. See licence.md for more information.
+// Copyright 2015 MIT Licence. See licence.md for more information.
//
// -----------------------------------------------------------------------
@@ -13,52 +13,97 @@ namespace Perspex.Controls
using Perspex.Controls.Templates;
using Perspex.Layout;
- public class ContentControl : TemplatedControl, IContentControl
+ ///
+ /// Displays according to a .
+ ///
+ public class ContentControl : TemplatedControl, IContentControl, IReparentingHost
{
+ ///
+ /// Defines the property.
+ ///
public static readonly PerspexProperty ContentProperty =
- PerspexProperty.Register("Content");
+ PerspexProperty.Register(nameof(Content));
+ ///
+ /// Defines the property.
+ ///
public static readonly PerspexProperty HorizontalContentAlignmentProperty =
- PerspexProperty.Register("HorizontalContentAlignment");
+ PerspexProperty.Register(nameof(HorizontalContentAlignment));
+ ///
+ /// Defines the property.
+ ///
public static readonly PerspexProperty VerticalContentAlignmentProperty =
- PerspexProperty.Register("VerticalContentAlignment");
+ PerspexProperty.Register(nameof(VerticalContentAlignment));
+ ///
+ /// Initializes a new instance of the class.
+ ///
public ContentControl()
{
}
+ ///
+ /// Gets or sets the content to display.
+ ///
public object Content
{
get { return this.GetValue(ContentProperty); }
set { this.SetValue(ContentProperty, value); }
}
+ ///
+ /// Gets the presenter from the control's template.
+ ///
public ContentPresenter Presenter
{
get;
private set;
}
+ ///
+ /// Gets or sets the horizontal alignment of the content within the control.
+ ///
public HorizontalAlignment HorizontalContentAlignment
{
get { return this.GetValue(HorizontalContentAlignmentProperty); }
set { this.SetValue(HorizontalContentAlignmentProperty, value); }
}
+ ///
+ /// Gets or sets the vertical alignment of the content within the control.
+ ///
public VerticalAlignment VerticalContentAlignment
{
get { return this.GetValue(VerticalContentAlignmentProperty); }
set { this.SetValue(VerticalContentAlignmentProperty, value); }
}
+ ///
+ /// Gets a writeable logical children collection from the host.
+ ///
+ IPerspexList IReparentingHost.LogicalChildren => this.LogicalChildren;
+
+ ///
+ /// Asks the control whether it wants to reparent the logical children of the specified
+ /// control.
+ ///
+ /// The control.
+ ///
+ /// True if the control wants to reparent its logical children otherwise false.
+ ///
+ bool IReparentingHost.WillReparentChildrenOf(IControl control)
+ {
+ return control is IContentPresenter && control.TemplatedParent == this;
+ }
+
+ ///
protected override void OnTemplateApplied()
{
// 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.
this.Presenter = this.FindTemplateChild("contentPresenter");
- ((IReparentingControl)this.Presenter)?.ReparentLogicalChildren(this, this.LogicalChildren);
}
}
}
diff --git a/Perspex.Controls/DropDown.cs b/Perspex.Controls/DropDown.cs
index a4d736b9b4..bdc7d9b588 100644
--- a/Perspex.Controls/DropDown.cs
+++ b/Perspex.Controls/DropDown.cs
@@ -69,7 +69,6 @@ namespace Perspex.Controls
protected override void OnTemplateApplied()
{
var container = this.GetTemplateChild("container");
- ((IReparentingControl)container).ReparentLogicalChildren(this, this.LogicalChildren);
}
private void SetContentParent(Tuple change)
diff --git a/Perspex.Controls/IReparentingHost.cs b/Perspex.Controls/IReparentingHost.cs
new file mode 100644
index 0000000000..7f0b00a242
--- /dev/null
+++ b/Perspex.Controls/IReparentingHost.cs
@@ -0,0 +1,31 @@
+// -----------------------------------------------------------------------
+//
+// Copyright 2015 MIT Licence. See licence.md for more information.
+//
+// -----------------------------------------------------------------------
+
+namespace Perspex.Controls
+{
+ using Perspex.Collections;
+
+ ///
+ /// A control that can use the visual children of another control as its logical children.
+ ///
+ public interface IReparentingHost : ILogical
+ {
+ ///
+ /// Gets a writeable logical children collection from the host.
+ ///
+ new IPerspexList LogicalChildren { get; }
+
+ ///
+ /// Asks the control whether it wants to reparent the logical children of the specified
+ /// control.
+ ///
+ /// The control.
+ ///
+ /// True if the control wants to reparent its logical children otherwise false.
+ ///
+ bool WillReparentChildrenOf(IControl control);
+ }
+}
\ No newline at end of file
diff --git a/Perspex.Controls/ItemsControl.cs b/Perspex.Controls/ItemsControl.cs
index fb83aeab64..4dab49dd98 100644
--- a/Perspex.Controls/ItemsControl.cs
+++ b/Perspex.Controls/ItemsControl.cs
@@ -22,7 +22,7 @@ namespace Perspex.Controls
///
/// Displays a collection of items.
///
- public class ItemsControl : TemplatedControl
+ public class ItemsControl : TemplatedControl, IReparentingHost
{
///
/// The default value for the property.
@@ -45,8 +45,6 @@ namespace Perspex.Controls
private IItemContainerGenerator itemContainerGenerator;
- private IItemsPresenter presenter;
-
///
/// Initializes static members of the class.
///
@@ -102,16 +100,27 @@ namespace Perspex.Controls
///
public IItemsPresenter Presenter
{
- get
- {
- return this.presenter;
- }
+ get;
+ set;
+ }
- protected set
- {
- this.presenter = value;
- (value as IReparentingControl)?.ReparentLogicalChildren(this, this.LogicalChildren);
- }
+ ///
+ IPerspexList IReparentingHost.LogicalChildren
+ {
+ get { return this.LogicalChildren; }
+ }
+
+ ///
+ /// Asks the control whether it wants to reparent the logical children of the specified
+ /// control.
+ ///
+ /// The control.
+ ///
+ /// True if the control wants to reparent its logical children otherwise false.
+ ///
+ bool IReparentingHost.WillReparentChildrenOf(IControl control)
+ {
+ return control is IItemsPresenter && control.TemplatedParent == this;
}
///
diff --git a/Perspex.Controls/Perspex.Controls.csproj b/Perspex.Controls/Perspex.Controls.csproj
index 907abbdef2..94392d744f 100644
--- a/Perspex.Controls/Perspex.Controls.csproj
+++ b/Perspex.Controls/Perspex.Controls.csproj
@@ -39,10 +39,12 @@
+
+
diff --git a/Perspex.Controls/Presenters/ContentPresenter.cs b/Perspex.Controls/Presenters/ContentPresenter.cs
index 36385428d7..bb030530ec 100644
--- a/Perspex.Controls/Presenters/ContentPresenter.cs
+++ b/Perspex.Controls/Presenters/ContentPresenter.cs
@@ -6,16 +6,12 @@
namespace Perspex.Controls.Presenters
{
- using System.Linq;
- using System.Reactive.Linq;
- using Perspex.Collections;
- using Perspex.Controls.Primitives;
using Perspex.Controls.Templates;
///
/// Presents a single item of data inside a template.
///
- public class ContentPresenter : Control, IPresenter
+ public class ContentPresenter : Control, IContentPresenter
{
///
/// Defines the property.
@@ -25,8 +21,6 @@ namespace Perspex.Controls.Presenters
private bool createdChild;
- private ILogical logicalParent;
-
///
/// Initializes static members of the class.
///
@@ -40,7 +34,8 @@ namespace Perspex.Controls.Presenters
///
public IControl Child
{
- get { return (Control)this.LogicalChildren.SingleOrDefault(); }
+ get;
+ private set;
}
///
@@ -61,20 +56,6 @@ namespace Perspex.Controls.Presenters
}
}
- ///
- void IReparentingControl.ReparentLogicalChildren(ILogical logicalParent, IPerspexList children)
- {
- if (this.Child != null)
- {
- ((ISetLogicalParent)this.Child).SetParent(null);
- ((ISetLogicalParent)this.Child).SetParent(logicalParent);
- children.Add(this.Child);
- }
-
- this.logicalParent = logicalParent;
- this.RedirectLogicalChildren(children);
- }
-
///
protected override Size MeasureCore(Size availableSize)
{
@@ -110,23 +91,29 @@ namespace Perspex.Controls.Presenters
///
private void CreateChild()
{
- IControl result = null;
- object content = this.Content;
+ var old = this.Child;
+ var content = this.Content;
+ var result = content != null ? this.MaterializeDataTemplate(content) : null;
+ var logicalHost = this.FindReparentingHost();
+ var logicalChildren = logicalHost?.LogicalChildren ?? this.LogicalChildren;
- this.LogicalChildren.Clear();
+ logicalChildren.Remove(old);
this.ClearVisualChildren();
- if (content != null)
+ this.Child = result;
+
+ if (result != null)
{
- result = this.MaterializeDataTemplate(content);
+ this.AddVisualChild(result);
if (result.Parent == null)
{
- ((ISetLogicalParent)result).SetParent(this.logicalParent ?? this);
+ ((ISetLogicalParent)result).SetParent((ILogical)logicalHost ?? this);
+
}
- this.AddVisualChild(result);
- this.LogicalChildren.Add(result);
+ logicalChildren.Remove(old);
+ logicalChildren.Add(result);
}
this.createdChild = true;
diff --git a/Perspex.Controls/Presenters/DeckPresenter.cs b/Perspex.Controls/Presenters/DeckPresenter.cs
index 5f25ffd332..b8a24c85e9 100644
--- a/Perspex.Controls/Presenters/DeckPresenter.cs
+++ b/Perspex.Controls/Presenters/DeckPresenter.cs
@@ -15,6 +15,7 @@ namespace Perspex.Controls.Presenters
using Perspex.Collections;
using Perspex.Controls.Generators;
using Perspex.Controls.Primitives;
+ using Templates;
using Perspex.Controls.Utils;
///
@@ -140,28 +141,28 @@ namespace Perspex.Controls.Presenters
}
}
- ///
- void IReparentingControl.ReparentLogicalChildren(ILogical logicalParent, IPerspexList children)
- {
- this.ApplyTemplate();
- ((IReparentingControl)this.Panel).ReparentLogicalChildren(logicalParent, children);
- }
-
///
/// Creates the .
///
private void CreatePanel()
{
+ var logicalHost = this.FindReparentingHost();
+
this.ClearVisualChildren();
+ this.Panel = this.ItemsPanel.Build();
+ this.Panel.SetValue(TemplatedParentProperty, this.TemplatedParent);
+
+ this.AddVisualChild(this.Panel);
- if (this.ItemsPanel != null)
+ if (logicalHost != null)
{
- this.Panel = this.ItemsPanel.Build();
- this.Panel.TemplatedParent = this.TemplatedParent;
- this.AddVisualChild(this.Panel);
- this.createdPanel = true;
- var task = this.MoveToPage(-1, this.SelectedIndex);
+ ((IReparentingControl)this.Panel).ReparentLogicalChildren(
+ logicalHost,
+ logicalHost.LogicalChildren);
}
+
+ this.createdPanel = true;
+ var task = this.MoveToPage(-1, this.SelectedIndex);
}
///
diff --git a/Perspex.Controls/Presenters/IContentPresenter.cs b/Perspex.Controls/Presenters/IContentPresenter.cs
new file mode 100644
index 0000000000..b460fc124f
--- /dev/null
+++ b/Perspex.Controls/Presenters/IContentPresenter.cs
@@ -0,0 +1,25 @@
+// -----------------------------------------------------------------------
+//
+// Copyright 2015 MIT Licence. See licence.md for more information.
+//
+// -----------------------------------------------------------------------
+
+namespace Perspex.Controls.Presenters
+{
+ ///
+ /// Interface for controls that present a single item of data inside a
+ /// template.
+ ///
+ public interface IContentPresenter : IPresenter
+ {
+ ///
+ /// Gets the control displayed by the presenter.
+ ///
+ IControl Child { get; }
+
+ ///
+ /// Gets or sets the content to be displayed by the presenter.
+ ///
+ object Content { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/Perspex.Controls/Presenters/IPresenter.cs b/Perspex.Controls/Presenters/IPresenter.cs
index 1638ce8f13..3de04e2af4 100644
--- a/Perspex.Controls/Presenters/IPresenter.cs
+++ b/Perspex.Controls/Presenters/IPresenter.cs
@@ -18,7 +18,7 @@ namespace Perspex.Controls.Presenters
/// of a then that signals that the visual child
/// of the presenter is not a part of the template.
///
- public interface IPresenter : IControl, INamed, IReparentingControl
+ public interface IPresenter : IControl, INamed
{
}
}
diff --git a/Perspex.Controls/Presenters/ItemsPresenter.cs b/Perspex.Controls/Presenters/ItemsPresenter.cs
index 432dd66d65..533f4af22e 100644
--- a/Perspex.Controls/Presenters/ItemsPresenter.cs
+++ b/Perspex.Controls/Presenters/ItemsPresenter.cs
@@ -9,15 +9,15 @@ namespace Perspex.Controls.Presenters
using System;
using System.Collections;
using System.Collections.Specialized;
- using Collections;
using Perspex.Controls.Generators;
+ using Templates;
using Perspex.Input;
using Perspex.Styling;
///
/// Displays items inside an .
///
- public class ItemsPresenter : Control, IItemsPresenter, ITemplatedControl, IReparentingControl
+ public class ItemsPresenter : Control, IItemsPresenter, ITemplatedControl
{
///
/// Defines the property.
@@ -110,13 +110,6 @@ namespace Perspex.Controls.Presenters
}
}
- ///
- public void ReparentLogicalChildren(ILogical logicalParent, IPerspexList children)
- {
- this.ApplyTemplate();
- ((IReparentingControl)this.Panel).ReparentLogicalChildren(logicalParent, children);
- }
-
///
protected override Size MeasureOverride(Size availableSize)
{
@@ -140,8 +133,19 @@ namespace Perspex.Controls.Presenters
this.ClearVisualChildren();
this.Panel = this.ItemsPanel.Build();
this.Panel.SetValue(TemplatedParentProperty, this.TemplatedParent);
- KeyboardNavigation.SetTabNavigation(this.Panel, KeyboardNavigation.GetTabNavigation(this));
+
this.AddVisualChild(this.Panel);
+
+ var logicalHost = this.FindReparentingHost();
+
+ if (logicalHost != null)
+ {
+ ((IReparentingControl)this.Panel).ReparentLogicalChildren(
+ logicalHost,
+ logicalHost.LogicalChildren);
+ }
+
+ KeyboardNavigation.SetTabNavigation(this.Panel, KeyboardNavigation.GetTabNavigation(this));
this.createdPanel = true;
this.CreateItemsAndListenForChanges(this.Items);
}
diff --git a/Perspex.Controls/TabControl.cs b/Perspex.Controls/TabControl.cs
index e1baf59f09..77e686e889 100644
--- a/Perspex.Controls/TabControl.cs
+++ b/Perspex.Controls/TabControl.cs
@@ -7,13 +7,13 @@
namespace Perspex.Controls
{
using Perspex.Animation;
+ using Perspex.Controls.Presenters;
using Perspex.Controls.Primitives;
- using Perspex.Controls.Templates;
///
/// A tab control that displays a tab strip along with the content of the selected tab.
///
- public class TabControl : SelectingItemsControl
+ public class TabControl : SelectingItemsControl, IReparentingHost
{
///
/// Defines the property.
@@ -70,15 +70,17 @@ namespace Perspex.Controls
set { this.SetValue(TransitionProperty, value); }
}
- ///
- protected override void OnTemplateApplied()
+ ///
+ /// Asks the control whether it wants to reparent the logical children of the specified
+ /// control.
+ ///
+ /// The control.
+ ///
+ /// True if the control wants to reparent its logical children otherwise false.
+ ///
+ bool IReparentingHost.WillReparentChildrenOf(IControl control)
{
- base.OnTemplateApplied();
-
- var deck = this.GetTemplateChild("deck");
- ((IReparentingControl)deck.Presenter.Panel).ReparentLogicalChildren(
- this,
- this.LogicalChildren);
+ return control is DeckPresenter;
}
///
diff --git a/Perspex.Controls/Templates/TemplateExtensions.cs b/Perspex.Controls/Templates/TemplateExtensions.cs
index de77d7f73a..d53c36540d 100644
--- a/Perspex.Controls/Templates/TemplateExtensions.cs
+++ b/Perspex.Controls/Templates/TemplateExtensions.cs
@@ -15,6 +15,35 @@ namespace Perspex.Controls.Templates
public static class TemplateExtensions
{
+ public static IReparentingHost FindReparentingHost(this IControl control)
+ {
+ var tp = control.TemplatedParent;
+ var chain = new List();
+
+ while (tp != null)
+ {
+ var reparentingHost = tp as IReparentingHost;
+ var styleable = tp as IStyleable;
+
+ if (reparentingHost != null)
+ {
+ chain.Add(reparentingHost);
+ }
+
+ tp = styleable?.TemplatedParent ?? null;
+ }
+
+ foreach (var reparenting in chain.AsEnumerable().Reverse())
+ {
+ if (reparenting.WillReparentChildrenOf(control))
+ {
+ return reparenting;
+ }
+ }
+
+ return null;
+ }
+
public static T FindTemplateChild(this ITemplatedControl control, string id) where T : INamed
{
return control.GetTemplateChildren().OfType().SingleOrDefault(x => x.Name == id);
diff --git a/Perspex.Themes.Default/TabStripStyle.cs b/Perspex.Themes.Default/TabStripStyle.cs
index af01a74d21..ca233142bc 100644
--- a/Perspex.Themes.Default/TabStripStyle.cs
+++ b/Perspex.Themes.Default/TabStripStyle.cs
@@ -26,7 +26,7 @@ namespace Perspex.Themes.Default
new Setter(TabStrip.TemplateProperty, new ControlTemplate(this.Template)),
},
},
- new Style(x => x.OfType().Template().OfType().Template().OfType())
+ new Style(x => x.OfType().Template().OfType())
{
Setters = new[]
{
diff --git a/Tests/Perspex.Controls.UnitTests/ContentControlTests.cs b/Tests/Perspex.Controls.UnitTests/ContentControlTests.cs
index f8c9179a5f..c7a59ba444 100644
--- a/Tests/Perspex.Controls.UnitTests/ContentControlTests.cs
+++ b/Tests/Perspex.Controls.UnitTests/ContentControlTests.cs
@@ -218,8 +218,7 @@ namespace Perspex.Controls.UnitTests
target.Content = child;
target.ApplyTemplate();
- ((ILogical)target).LogicalChildren.CollectionChanged += (s, e) =>
- called = e.Action == NotifyCollectionChangedAction.Remove;
+ ((ILogical)target).LogicalChildren.CollectionChanged += (s, e) => called = true;
target.Content = null;
diff --git a/Tests/Perspex.Controls.UnitTests/TabControlTests.cs b/Tests/Perspex.Controls.UnitTests/TabControlTests.cs
index 96014ba7da..4f7f224d19 100644
--- a/Tests/Perspex.Controls.UnitTests/TabControlTests.cs
+++ b/Tests/Perspex.Controls.UnitTests/TabControlTests.cs
@@ -41,33 +41,6 @@ namespace Perspex.Controls.UnitTests
Assert.Equal(selected, target.SelectedItem);
Assert.Equal(selected, target.SelectedTab);
- Assert.Equal("foo", target.SelectedContent);
- }
-
- [Fact]
- public void SelectedContent_Should_Initially_Be_First_Tab_Content()
- {
- var target = new TabControl
- {
- Template = new ControlTemplate(this.CreateTabControlTemplate),
- Items = new[]
- {
- new TabItem
- {
- Name = "first",
- Content = "foo",
- },
- new TabItem
- {
- Name = "second",
- Content = "bar",
- },
- }
- };
-
- target.ApplyTemplate();
-
- Assert.Equal("foo", target.SelectedContent);
}
[Fact]
@@ -95,7 +68,6 @@ namespace Perspex.Controls.UnitTests
target.SelectedItem = target.Items.Cast().ElementAt(1);
Assert.Same(target.SelectedTab, target.SelectedItem);
- Assert.Equal("bar", target.SelectedContent);
}
[Fact]