diff --git a/src/Perspex.Controls/ItemsControl.cs b/src/Perspex.Controls/ItemsControl.cs
index 16873b7f05..1c6a51529f 100644
--- a/src/Perspex.Controls/ItemsControl.cs
+++ b/src/Perspex.Controls/ItemsControl.cs
@@ -21,7 +21,7 @@ namespace Perspex.Controls
///
/// Displays a collection of items.
///
- public class ItemsControl : TemplatedControl
+ public class ItemsControl : TemplatedControl, IItemsPresenterHost
{
///
/// The default value for the property.
@@ -127,6 +127,12 @@ namespace Perspex.Controls
protected set;
}
+ ///
+ void IItemsPresenterHost.RegisterItemsPresenter(IItemsPresenter presenter)
+ {
+ Presenter = presenter;
+ }
+
///
/// Gets the item at the specified index in a collection.
///
@@ -243,13 +249,6 @@ namespace Perspex.Controls
LogicalChildren.RemoveAll(toRemove);
}
- ///
- protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
- {
- base.OnTemplateApplied(e);
- Presenter = e.NameScope.Find("PART_ItemsPresenter");
- }
-
///
protected override void OnTemplateChanged(PerspexPropertyChangedEventArgs e)
{
diff --git a/src/Perspex.Controls/Mixins/ContentControlMixin.cs b/src/Perspex.Controls/Mixins/ContentControlMixin.cs
index 65153ed0af..e19cc17ae2 100644
--- a/src/Perspex.Controls/Mixins/ContentControlMixin.cs
+++ b/src/Perspex.Controls/Mixins/ContentControlMixin.cs
@@ -8,6 +8,7 @@ using Perspex.Collections;
using Perspex.Controls.Presenters;
using Perspex.Controls.Primitives;
using Perspex.Interactivity;
+using Perspex.Styling;
namespace Perspex.Controls.Mixins
{
@@ -62,6 +63,7 @@ namespace Perspex.Controls.Mixins
var subscription = presenter
.GetObservable(ContentPresenter.ChildProperty)
.Subscribe(child => UpdateLogicalChild(
+ sender,
logicalChildren,
logicalChildren.FirstOrDefault(),
child));
@@ -82,7 +84,18 @@ namespace Perspex.Controls.Mixins
if (sender != null)
{
var logicalChildren = logicalChildrenSelector(sender);
- UpdateLogicalChild(logicalChildren, e.OldValue, e.NewValue);
+ UpdateLogicalChild(sender, logicalChildren, e.OldValue, e.NewValue);
+ }
+ });
+
+ Control.TemplatedParentProperty.Changed.Subscribe(e =>
+ {
+ var sender = e.Sender as TControl;
+
+ if (sender != null)
+ {
+ var logicalChild = logicalChildrenSelector(sender).FirstOrDefault() as IControl;
+ logicalChild?.SetValue(Control.TemplatedParentProperty, sender.TemplatedParent);
}
});
@@ -111,24 +124,26 @@ namespace Perspex.Controls.Mixins
}
private static void UpdateLogicalChild(
+ IControl control,
IPerspexList logicalChildren,
object oldValue,
object newValue)
{
if (oldValue != newValue)
{
- var logical = oldValue as ILogical;
+ var child = oldValue as IControl;
- if (logical != null)
+ if (child != null)
{
- logicalChildren.Remove(logical);
+ logicalChildren.Remove(child);
}
- logical = newValue as ILogical;
+ child = newValue as IControl;
- if (logical != null)
+ if (child != null)
{
- logicalChildren.Add(logical);
+ child.SetValue(Control.TemplatedParentProperty, control.TemplatedParent);
+ logicalChildren.Add(child);
}
}
}
diff --git a/src/Perspex.Controls/Perspex.Controls.csproj b/src/Perspex.Controls/Perspex.Controls.csproj
index c0fbb8fab3..a0ea1e4084 100644
--- a/src/Perspex.Controls/Perspex.Controls.csproj
+++ b/src/Perspex.Controls/Perspex.Controls.csproj
@@ -57,6 +57,7 @@
+
diff --git a/src/Perspex.Controls/Presenters/CarouselPresenter.cs b/src/Perspex.Controls/Presenters/CarouselPresenter.cs
index cee9a053db..bd9d7b8840 100644
--- a/src/Perspex.Controls/Presenters/CarouselPresenter.cs
+++ b/src/Perspex.Controls/Presenters/CarouselPresenter.cs
@@ -64,6 +64,7 @@ namespace Perspex.Controls.Presenters
static CarouselPresenter()
{
SelectedIndexProperty.Changed.AddClassHandler(x => x.SelectedIndexChanged);
+ TemplatedParentProperty.Changed.AddClassHandler(x => x.TemplatedParentChanged);
}
///
@@ -256,5 +257,10 @@ namespace Perspex.Controls.Presenters
}
}
}
+
+ private void TemplatedParentChanged(PerspexPropertyChangedEventArgs e)
+ {
+ (e.NewValue as IItemsPresenterHost)?.RegisterItemsPresenter(this);
+ }
}
}
diff --git a/src/Perspex.Controls/Presenters/ContentPresenter.cs b/src/Perspex.Controls/Presenters/ContentPresenter.cs
index 0faa7fd783..fbc2300d02 100644
--- a/src/Perspex.Controls/Presenters/ContentPresenter.cs
+++ b/src/Perspex.Controls/Presenters/ContentPresenter.cs
@@ -57,12 +57,58 @@ namespace Perspex.Controls.Presenters
///
public override sealed void ApplyTemplate()
{
- if (!_createdChild)
+ if (!_createdChild && ((ILogical)this).IsAttachedToLogicalTree)
{
- CreateChild();
+ UpdateChild();
}
}
+ ///
+ /// Updates the control based on the control's .
+ ///
+ ///
+ /// Usually the control is created automatically when
+ /// is called; however for this to happen, the control needs to
+ /// be attached to a logical tree (if the control is not attached to the logical tree, it
+ /// is reasonable to expect that the DataTemplates needed for the child are not yet
+ /// available). This method forces the control's creation at any point,
+ /// and is particularly useful in unit tests.
+ ///
+ public void UpdateChild()
+ {
+ var old = Child;
+ var content = Content;
+ var result = this.MaterializeDataTemplate(content);
+
+ if (old != null)
+ {
+ VisualChildren.Remove(old);
+ }
+
+ if (result != null)
+ {
+ if (!(content is IControl))
+ {
+ result.DataContext = content;
+ }
+
+ Child = result;
+
+ if (result.Parent == null)
+ {
+ ((ISetLogicalParent)result).SetParent((ILogical)this.TemplatedParent ?? this);
+ }
+
+ VisualChildren.Add(result);
+ }
+ else
+ {
+ Child = null;
+ }
+
+ _createdChild = true;
+ }
+
///
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
{
@@ -100,37 +146,5 @@ namespace Perspex.Controls.Presenters
_createdChild = false;
InvalidateMeasure();
}
-
- ///
- /// Creates the control from the .
- ///
- private void CreateChild()
- {
- var old = Child;
- var content = Content;
- var result = this.MaterializeDataTemplate(content);
-
- if (old != null)
- {
- VisualChildren.Remove(old);
- }
-
- if (result != null)
- {
- if (!(content is IControl))
- {
- result.DataContext = content;
- }
-
- Child = result;
- VisualChildren.Add(result);
- }
- else
- {
- Child = null;
- }
-
- _createdChild = true;
- }
}
}
diff --git a/src/Perspex.Controls/Presenters/IItemsPresenterHost.cs b/src/Perspex.Controls/Presenters/IItemsPresenterHost.cs
new file mode 100644
index 0000000000..47f50eb862
--- /dev/null
+++ b/src/Perspex.Controls/Presenters/IItemsPresenterHost.cs
@@ -0,0 +1,27 @@
+// 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 Perspex.Styling;
+
+namespace Perspex.Controls.Presenters
+{
+ ///
+ /// Represents a control which hosts an items presenter.
+ ///
+ ///
+ /// This interface is implemented by which usually contains an
+ /// and exposes it through its
+ /// property. ItemsPresenters can be within
+ /// nested templates or in popups and so are not necessarily created immediately when the
+ /// parent control's template is instantiated so they register themselves using this
+ /// interface.
+ ///
+ public interface IItemsPresenterHost : ITemplatedControl
+ {
+ ///
+ /// Registers an with a host control.
+ ///
+ /// The items presenter.
+ void RegisterItemsPresenter(IItemsPresenter presenter);
+ }
+}
diff --git a/src/Perspex.Controls/Presenters/ItemsPresenter.cs b/src/Perspex.Controls/Presenters/ItemsPresenter.cs
index 6f052af7f7..c2eb692b7a 100644
--- a/src/Perspex.Controls/Presenters/ItemsPresenter.cs
+++ b/src/Perspex.Controls/Presenters/ItemsPresenter.cs
@@ -49,6 +49,7 @@ namespace Perspex.Controls.Presenters
typeof(ItemsPresenter),
KeyboardNavigationMode.Once);
ItemsProperty.Changed.AddClassHandler(x => x.ItemsChanged);
+ TemplatedParentProperty.Changed.AddClassHandler(x => x.TemplatedParentChanged);
}
///
@@ -270,6 +271,11 @@ namespace Perspex.Controls.Presenters
}
}
+ private void TemplatedParentChanged(PerspexPropertyChangedEventArgs e)
+ {
+ (e.NewValue as IItemsPresenterHost)?.RegisterItemsPresenter(this);
+ }
+
private void AddContainers(IEnumerable items)
{
foreach (var i in items)
diff --git a/src/Perspex.Controls/Primitives/PopupRoot.cs b/src/Perspex.Controls/Primitives/PopupRoot.cs
index 0847780bd8..5347ef9180 100644
--- a/src/Perspex.Controls/Primitives/PopupRoot.cs
+++ b/src/Perspex.Controls/Primitives/PopupRoot.cs
@@ -96,6 +96,7 @@ namespace Perspex.Controls.Primitives
_presenterSubscription = null;
}
+ Presenter?.ApplyTemplate();
Presenter?.GetObservable(ContentPresenter.ChildProperty)
.Subscribe(SetTemplatedParentAndApplyChildTemplates);
}
diff --git a/src/Perspex.Controls/Primitives/TemplatedControl.cs b/src/Perspex.Controls/Primitives/TemplatedControl.cs
index 09b90a2e1e..e0bd785e97 100644
--- a/src/Perspex.Controls/Primitives/TemplatedControl.cs
+++ b/src/Perspex.Controls/Primitives/TemplatedControl.cs
@@ -212,20 +212,9 @@ namespace Perspex.Controls.Primitives
var child = Template.Build(this);
var nameScope = new NameScope();
NameScope.SetNameScope((Control)child, nameScope);
-
- // We need to call SetupTemplateControls twice:
- // - Once before the controls are added to the visual/logical trees so that the
- // TemplatedParent property is set and names are registered; if
- // TemplatedParent is not set when the control is added to the logical tree,
- // then styles with the /template/ selector won't match.
- // - Once after the controls are added to the logical tree (and thus styled) to
- // call ApplyTemplate on nested templated controls and register any of our
- // templated children that appear as children of presenters in these nested
- // templated child controls.
SetupTemplateControls(child, nameScope);
- VisualChildren.Add(child);
((ISetLogicalParent)child).SetParent(this);
- SetupTemplateControls(child, nameScope);
+ VisualChildren.Add(child);
OnTemplateApplied(new TemplateAppliedEventArgs(nameScope));
}
@@ -290,8 +279,6 @@ namespace Perspex.Controls.Primitives
}
}
- control.ApplyTemplate();
-
if (!(control is IPresenter && control.TemplatedParent == this))
{
foreach (IControl child in control.GetVisualChildren())
diff --git a/src/Perspex.Controls/ScrollViewer.cs b/src/Perspex.Controls/ScrollViewer.cs
index f8a712cd6a..6391918325 100644
--- a/src/Perspex.Controls/ScrollViewer.cs
+++ b/src/Perspex.Controls/ScrollViewer.cs
@@ -120,8 +120,6 @@ namespace Perspex.Controls
nameof(VerticalScrollBarVisibility),
ScrollBarVisibility.Auto);
- private IDisposable _scrollableSubscription;
-
///
/// Initializes static members of the class.
///
diff --git a/tests/Perspex.Controls.UnitTests/CarouselTests.cs b/tests/Perspex.Controls.UnitTests/CarouselTests.cs
index b1a0a871cd..1fd78655e1 100644
--- a/tests/Perspex.Controls.UnitTests/CarouselTests.cs
+++ b/tests/Perspex.Controls.UnitTests/CarouselTests.cs
@@ -45,6 +45,7 @@ namespace Perspex.Controls.UnitTests
};
target.ApplyTemplate();
+ target.Presenter.ApplyTemplate();
Assert.Equal(1, target.GetLogicalChildren().Count());
@@ -57,7 +58,7 @@ namespace Perspex.Controls.UnitTests
{
return new CarouselPresenter
{
- Name = "itemsPresenter",
+ Name = "PART_ItemsPresenter",
[~ItemsPresenter.ItemsProperty] = control[~ItemsControl.ItemsProperty],
[~ItemsPresenter.ItemsPanelProperty] = control[~ItemsControl.ItemsPanelProperty],
[~CarouselPresenter.SelectedIndexProperty] = control[~SelectingItemsControl.SelectedIndexProperty],
diff --git a/tests/Perspex.Controls.UnitTests/ContentControlTests.cs b/tests/Perspex.Controls.UnitTests/ContentControlTests.cs
index ff0c993af6..a74eabe9e7 100644
--- a/tests/Perspex.Controls.UnitTests/ContentControlTests.cs
+++ b/tests/Perspex.Controls.UnitTests/ContentControlTests.cs
@@ -27,8 +27,8 @@ namespace Perspex.Controls.UnitTests
var target = new ContentControl();
target.Content = "Foo";
target.Template = GetTemplate();
-
- target.Measure(new Size(100, 100));
+ target.ApplyTemplate();
+ target.Presenter.UpdateChild();
var child = ((IVisual)target).VisualChildren.Single();
Assert.IsType(child);
@@ -54,6 +54,7 @@ namespace Perspex.Controls.UnitTests
root.Child = target;
target.ApplyTemplate();
+ target.Presenter.UpdateChild();
styler.Verify(x => x.ApplyStyles(It.IsAny()), Times.Once());
styler.Verify(x => x.ApplyStyles(It.IsAny()), Times.Once());
@@ -71,6 +72,7 @@ namespace Perspex.Controls.UnitTests
target.Template = GetTemplate();
target.Content = child;
target.ApplyTemplate();
+ target.Presenter.UpdateChild();
var contentPresenter = child.GetVisualParent();
Assert.Equal(target, contentPresenter.TemplatedParent);
@@ -85,6 +87,7 @@ namespace Perspex.Controls.UnitTests
target.Template = GetTemplate();
target.Content = child;
target.ApplyTemplate();
+ target.Presenter.UpdateChild();
Assert.Null(child.TemplatedParent);
}
@@ -115,6 +118,7 @@ namespace Perspex.Controls.UnitTests
target.Content = "Foo";
target.ApplyTemplate();
+ target.Presenter.UpdateChild();
var child = target.Presenter.Child;
@@ -131,6 +135,9 @@ namespace Perspex.Controls.UnitTests
var child = new Control();
target.Content = child;
+
+ Assert.Equal(new[] { child }, target.GetLogicalChildren());
+
target.Content = null;
Assert.Null(child.Parent);
@@ -151,7 +158,7 @@ namespace Perspex.Controls.UnitTests
target.Template = GetTemplate();
target.Content = child;
target.ApplyTemplate();
- target.Presenter.ApplyTemplate();
+ target.Presenter.UpdateChild();
Assert.True(called);
}
@@ -166,11 +173,12 @@ namespace Perspex.Controls.UnitTests
target.Template = GetTemplate();
target.Content = child;
target.ApplyTemplate();
+ target.Presenter.UpdateChild();
((ILogical)target).LogicalChildren.CollectionChanged += (s, e) => called = true;
target.Content = null;
- target.Presenter.ApplyTemplate();
+ target.Presenter.UpdateChild();
Assert.True(called);
}
@@ -178,19 +186,20 @@ namespace Perspex.Controls.UnitTests
[Fact]
public void Changing_Content_Should_Fire_LogicalChildren_CollectionChanged()
{
- var contentControl = new ContentControl();
+ var target = new ContentControl();
var child1 = new Control();
var child2 = new Control();
var called = false;
- contentControl.Template = GetTemplate();
- contentControl.Content = child1;
- contentControl.ApplyTemplate();
+ target.Template = GetTemplate();
+ target.Content = child1;
+ target.ApplyTemplate();
+ target.Presenter.UpdateChild();
- ((ILogical)contentControl).LogicalChildren.CollectionChanged += (s, e) => called = true;
+ ((ILogical)target).LogicalChildren.CollectionChanged += (s, e) => called = true;
- contentControl.Content = child2;
- contentControl.Presenter.ApplyTemplate();
+ target.Content = child2;
+ target.Presenter.ApplyTemplate();
Assert.True(called);
}
@@ -202,12 +211,13 @@ namespace Perspex.Controls.UnitTests
target.Template = GetTemplate();
target.ApplyTemplate();
+ target.Presenter.UpdateChild();
target.Content = "Foo";
- target.Presenter.ApplyTemplate();
+ target.Presenter.UpdateChild();
Assert.Equal("Foo", ((TextBlock)target.Presenter.Child).Text);
target.Content = "Bar";
- target.Presenter.ApplyTemplate();
+ target.Presenter.UpdateChild();
Assert.Equal("Bar", ((TextBlock)target.Presenter.Child).Text);
}
@@ -219,6 +229,7 @@ namespace Perspex.Controls.UnitTests
target.Template = GetTemplate();
target.Content = "Foo";
target.ApplyTemplate();
+ target.Presenter.UpdateChild();
Assert.Equal("Foo", target.Presenter.Child.DataContext);
}
@@ -231,6 +242,7 @@ namespace Perspex.Controls.UnitTests
target.Template = GetTemplate();
target.Content = new TextBlock();
target.ApplyTemplate();
+ target.Presenter.UpdateChild();
Assert.Null(target.Presenter.Child.DataContext);
}
diff --git a/tests/Perspex.Controls.UnitTests/ControlTests_NameScope.cs b/tests/Perspex.Controls.UnitTests/ControlTests_NameScope.cs
index ec5f657877..57d0a0a7e7 100644
--- a/tests/Perspex.Controls.UnitTests/ControlTests_NameScope.cs
+++ b/tests/Perspex.Controls.UnitTests/ControlTests_NameScope.cs
@@ -28,6 +28,7 @@ namespace Perspex.Controls.UnitTests
};
root.ApplyTemplate();
+ root.Presenter.UpdateChild();
Assert.Same(root.Find("foo"), root.Content);
Assert.Same(root.Find("bar"), ((Border)root.Content).Child);
diff --git a/tests/Perspex.Controls.UnitTests/HeaderedItemsControlTests .cs b/tests/Perspex.Controls.UnitTests/HeaderedItemsControlTests .cs
index 3eb4db00de..b0d61a1380 100644
--- a/tests/Perspex.Controls.UnitTests/HeaderedItemsControlTests .cs
+++ b/tests/Perspex.Controls.UnitTests/HeaderedItemsControlTests .cs
@@ -37,6 +37,7 @@ namespace Perspex.Controls.UnitTests
target.Header = "Foo";
target.ApplyTemplate();
+ target.HeaderPresenter.UpdateChild();
var child = target.HeaderPresenter.Child;
diff --git a/tests/Perspex.Controls.UnitTests/ItemsControlTests.cs b/tests/Perspex.Controls.UnitTests/ItemsControlTests.cs
index 9cc960a5c2..49aba552fc 100644
--- a/tests/Perspex.Controls.UnitTests/ItemsControlTests.cs
+++ b/tests/Perspex.Controls.UnitTests/ItemsControlTests.cs
@@ -22,6 +22,7 @@ namespace Perspex.Controls.UnitTests
target.Template = GetTemplate();
target.Items = new[] { "Foo" };
target.ApplyTemplate();
+ target.Presenter.ApplyTemplate();
Assert.Equal(target, target.Presenter.Panel.TemplatedParent);
}
@@ -34,6 +35,7 @@ namespace Perspex.Controls.UnitTests
target.Template = GetTemplate();
target.Items = new[] { "Foo" };
target.ApplyTemplate();
+ target.Presenter.ApplyTemplate();
var item = (TextBlock)target.Presenter.Panel.GetVisualChildren().First();
@@ -125,6 +127,7 @@ namespace Perspex.Controls.UnitTests
target.Template = GetTemplate();
target.Items = new[] { "Foo" };
target.ApplyTemplate();
+ target.Presenter.ApplyTemplate();
var logical = (ILogical)target;
Assert.Equal(1, logical.LogicalChildren.Count);
@@ -140,6 +143,10 @@ namespace Perspex.Controls.UnitTests
target.Template = GetTemplate();
target.Items = new[] { "Foo" };
target.ApplyTemplate();
+ target.Presenter.ApplyTemplate();
+
+ Assert.NotEmpty(target.GetLogicalChildren());
+
target.Items = null;
Assert.Equal(new ILogical[0], target.GetLogicalChildren());
@@ -210,6 +217,7 @@ namespace Perspex.Controls.UnitTests
target.Template = GetTemplate();
target.Items = items;
target.ApplyTemplate();
+ target.Presenter.ApplyTemplate();
((ILogical)target).LogicalChildren.CollectionChanged += (s, e) =>
called = e.Action == NotifyCollectionChangedAction.Add;
@@ -229,6 +237,7 @@ namespace Perspex.Controls.UnitTests
target.Template = GetTemplate();
target.Items = items;
target.ApplyTemplate();
+ target.Presenter.ApplyTemplate();
((ILogical)target).LogicalChildren.CollectionChanged += (s, e) =>
called = e.Action == NotifyCollectionChangedAction.Remove;
@@ -339,6 +348,7 @@ namespace Perspex.Controls.UnitTests
};
target.ApplyTemplate();
+ target.Presenter.ApplyTemplate();
var dataContexts = target.Presenter.Panel.Children
.Cast()
@@ -361,6 +371,7 @@ namespace Perspex.Controls.UnitTests
};
target.ApplyTemplate();
+ target.Presenter.ApplyTemplate();
var text = target.Presenter.Panel.Children
.Cast()
diff --git a/tests/Perspex.Controls.UnitTests/ListBoxTests.cs b/tests/Perspex.Controls.UnitTests/ListBoxTests.cs
index 08c445e8df..eedd1a8682 100644
--- a/tests/Perspex.Controls.UnitTests/ListBoxTests.cs
+++ b/tests/Perspex.Controls.UnitTests/ListBoxTests.cs
@@ -7,12 +7,26 @@ using Perspex.Controls.Templates;
using Perspex.Input;
using Perspex.LogicalTree;
using Perspex.Styling;
+using Perspex.VisualTree;
using Xunit;
namespace Perspex.Controls.UnitTests
{
public class ListBoxTests
{
+ [Fact]
+ public void ListBox_Should_Find_ItemsPresenter_In_ScrollViewer()
+ {
+ var target = new ListBox
+ {
+ Template = new FuncControlTemplate(CreateListBoxTemplate),
+ };
+
+ ApplyTemplate(target);
+
+ Assert.IsType(target.Presenter);
+ }
+
[Fact]
public void ListBoxItem_Containers_Should_Be_Generated()
{
@@ -23,12 +37,12 @@ namespace Perspex.Controls.UnitTests
Items = items,
};
- target.ApplyTemplate();
+ ApplyTemplate(target);
var text = target.Presenter.Panel.Children
.OfType()
.Do(x => x.Template = ListBoxItemTemplate())
- .Do(x => x.ApplyTemplate())
+ .Do(x => { x.ApplyTemplate(); x.Presenter.UpdateChild(); })
.Select(x => x.Presenter.Child)
.OfType()
.Select(x => x.Text)
@@ -46,7 +60,7 @@ namespace Perspex.Controls.UnitTests
Items = new[] { "Foo", "Bar", "Baz " },
};
- target.ApplyTemplate();
+ ApplyTemplate(target);
Assert.Equal(3, target.GetLogicalChildren().Count());
@@ -78,7 +92,7 @@ namespace Perspex.Controls.UnitTests
Items = items,
};
- target.ApplyTemplate();
+ ApplyTemplate(target);
var dataContexts = target.Presenter.Panel.Children
.Cast()
@@ -99,7 +113,7 @@ namespace Perspex.Controls.UnitTests
Items = new[] { "Foo", "Bar", "Baz " },
};
- target.ApplyTemplate();
+ ApplyTemplate(target);
target.Presenter.Panel.Children[1].RaiseEvent(new PointerPressEventArgs
{
@@ -140,10 +154,27 @@ namespace Perspex.Controls.UnitTests
{
return new ScrollContentPresenter
{
+ Name = "PART_ContentPresenter",
[~ContentPresenter.ContentProperty] = parent.GetObservable(ContentControl.ContentProperty),
};
}
+ private void ApplyTemplate(ListBox target)
+ {
+ // Apply the template to the ListBox itself.
+ target.ApplyTemplate();
+
+ // Then to its inner ScrollViewer.
+ var scrollViewer = (ScrollViewer)target.GetVisualChildren().Single();
+ scrollViewer.ApplyTemplate();
+
+ // Then make the ScrollViewer create its child.
+ scrollViewer.Presenter.UpdateChild();
+
+ // Now the ItemsPresenter should be reigstered, so apply its template.
+ target.Presenter.ApplyTemplate();
+ }
+
private class Item
{
public Item(string value)
diff --git a/tests/Perspex.Controls.UnitTests/ListBoxTests_Single.cs b/tests/Perspex.Controls.UnitTests/ListBoxTests_Single.cs
index a6f02dc27b..4622d77159 100644
--- a/tests/Perspex.Controls.UnitTests/ListBoxTests_Single.cs
+++ b/tests/Perspex.Controls.UnitTests/ListBoxTests_Single.cs
@@ -7,6 +7,7 @@ using Perspex.Controls.Templates;
using Perspex.Input;
using Perspex.LogicalTree;
using Perspex.Styling;
+using Perspex.VisualTree;
using Xunit;
namespace Perspex.Controls.UnitTests
@@ -22,7 +23,7 @@ namespace Perspex.Controls.UnitTests
Items = new[] { "Foo", "Bar", "Baz " },
};
- target.ApplyTemplate();
+ ApplyTemplate(target);
target.Presenter.Panel.Children[0].RaiseEvent(new GotFocusEventArgs
{
@@ -42,7 +43,7 @@ namespace Perspex.Controls.UnitTests
Items = new[] { "Foo", "Bar", "Baz " },
};
- target.ApplyTemplate();
+ ApplyTemplate(target);
target.Presenter.Panel.Children[0].RaiseEvent(new GotFocusEventArgs
{
@@ -62,7 +63,7 @@ namespace Perspex.Controls.UnitTests
Items = new[] { "Foo", "Bar", "Baz " },
};
- target.ApplyTemplate();
+ ApplyTemplate(target);
target.Presenter.Panel.Children[0].RaiseEvent(new PointerPressEventArgs
{
@@ -82,7 +83,7 @@ namespace Perspex.Controls.UnitTests
Items = new[] { "Foo", "Bar", "Baz " },
};
- target.ApplyTemplate();
+ ApplyTemplate(target);
target.SelectedIndex = 0;
target.Presenter.Panel.Children[0].RaiseEvent(new PointerPressEventArgs
@@ -104,7 +105,7 @@ namespace Perspex.Controls.UnitTests
SelectionMode = SelectionMode.Single | SelectionMode.Toggle,
};
- target.ApplyTemplate();
+ ApplyTemplate(target);
target.Presenter.Panel.Children[0].RaiseEvent(new PointerPressEventArgs
{
@@ -125,7 +126,7 @@ namespace Perspex.Controls.UnitTests
SelectionMode = SelectionMode.Toggle,
};
- target.ApplyTemplate();
+ ApplyTemplate(target);
target.SelectedIndex = 0;
target.Presenter.Panel.Children[0].RaiseEvent(new PointerPressEventArgs
@@ -147,7 +148,7 @@ namespace Perspex.Controls.UnitTests
SelectionMode = SelectionMode.Toggle | SelectionMode.AlwaysSelected,
};
- target.ApplyTemplate();
+ ApplyTemplate(target);
target.SelectedIndex = 0;
target.Presenter.Panel.Children[0].RaiseEvent(new PointerPressEventArgs
@@ -169,7 +170,7 @@ namespace Perspex.Controls.UnitTests
SelectionMode = SelectionMode.Single | SelectionMode.Toggle,
};
- target.ApplyTemplate();
+ ApplyTemplate(target);
target.SelectedIndex = 1;
target.Presenter.Panel.Children[0].RaiseEvent(new PointerPressEventArgs
@@ -190,7 +191,7 @@ namespace Perspex.Controls.UnitTests
Items = new[] { "Foo", "Bar", "Baz " },
};
- target.ApplyTemplate();
+ ApplyTemplate(target);
((ListBoxItem)target.GetLogicalChildren().ElementAt(1)).IsSelected = true;
@@ -215,10 +216,27 @@ namespace Perspex.Controls.UnitTests
{
return new ScrollContentPresenter
{
+ Name = "PART_ContentPresenter",
[~ContentPresenter.ContentProperty] = parent.GetObservable(ContentControl.ContentProperty),
};
}
+ private void ApplyTemplate(ListBox target)
+ {
+ // Apply the template to the ListBox itself.
+ target.ApplyTemplate();
+
+ // Then to its inner ScrollViewer.
+ var scrollViewer = (ScrollViewer)target.GetVisualChildren().Single();
+ scrollViewer.ApplyTemplate();
+
+ // Then make the ScrollViewer create its child.
+ scrollViewer.Presenter.UpdateChild();
+
+ // Now the ItemsPresenter should be reigstered, so apply its template.
+ target.Presenter.ApplyTemplate();
+ }
+
private class Item
{
public Item(string value)
diff --git a/tests/Perspex.Controls.UnitTests/Presenters/CarouselPresenterTests.cs b/tests/Perspex.Controls.UnitTests/Presenters/CarouselPresenterTests.cs
index 7997e0cb90..3d715b93fb 100644
--- a/tests/Perspex.Controls.UnitTests/Presenters/CarouselPresenterTests.cs
+++ b/tests/Perspex.Controls.UnitTests/Presenters/CarouselPresenterTests.cs
@@ -1,6 +1,7 @@
// 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 Moq;
using Perspex.Controls.Generators;
using Perspex.Controls.Presenters;
using Perspex.Controls.Templates;
@@ -10,6 +11,17 @@ namespace Perspex.Controls.UnitTests.Presenters
{
public class CarouselPresenterTests
{
+ [Fact]
+ public void Should_Register_With_Host_When_TemplatedParent_Set()
+ {
+ var host = new Mock();
+ var target = new CarouselPresenter();
+
+ target.SetValue(Control.TemplatedParentProperty, host.Object);
+
+ host.Verify(x => x.RegisterItemsPresenter(target));
+ }
+
[Fact]
public void ApplyTemplate_Should_Create_Panel()
{
diff --git a/tests/Perspex.Controls.UnitTests/Presenters/ContentPresenterTests.cs b/tests/Perspex.Controls.UnitTests/Presenters/ContentPresenterTests.cs
index 3bc0465dc4..91b523a299 100644
--- a/tests/Perspex.Controls.UnitTests/Presenters/ContentPresenterTests.cs
+++ b/tests/Perspex.Controls.UnitTests/Presenters/ContentPresenterTests.cs
@@ -1,8 +1,11 @@
// 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.Linq;
using Perspex.Controls.Presenters;
+using Perspex.Controls.Primitives;
using Perspex.Controls.Templates;
+using Perspex.VisualTree;
using Xunit;
namespace Perspex.Controls.UnitTests.Presenters
@@ -17,11 +20,8 @@ namespace Perspex.Controls.UnitTests.Presenters
target.Content = child;
- // Child should not update until ApplyTemplate called.
Assert.Null(target.Child);
-
- target.ApplyTemplate();
-
+ target.UpdateChild();
Assert.Equal(child, target.Child);
}
@@ -32,13 +32,39 @@ namespace Perspex.Controls.UnitTests.Presenters
target.Content = "Foo";
- // Child should not update until ApplyTemplate called.
Assert.Null(target.Child);
+ target.UpdateChild();
+ Assert.IsType(target.Child);
+ Assert.Equal("Foo", ((TextBlock)target.Child).Text);
+ }
+
+ [Fact]
+ public void Should_Set_Childs_Parent_To_TemplatedParent()
+ {
+ var content = new Border();
+ var target = new TestContentControl
+ {
+ Template = new FuncControlTemplate(parent =>
+ new ContentPresenter { Content = parent.Child }),
+ Child = content,
+ };
target.ApplyTemplate();
+ var presenter = ((ContentPresenter)target.GetVisualChildren().Single());
+ presenter.UpdateChild();
- Assert.IsType(target.Child);
- Assert.Equal("Foo", ((TextBlock)target.Child).Text);
+ Assert.Same(target, content.Parent);
+ }
+
+ [Fact]
+ public void Should_Set_Childs_Parent_To_Itself_Outside_Template()
+ {
+ var content = new Border();
+ var target = new ContentPresenter { Content = content };
+
+ target.UpdateChild();
+
+ Assert.Same(target, content.Parent);
}
[Fact]
@@ -49,7 +75,7 @@ namespace Perspex.Controls.UnitTests.Presenters
Content = "Foo",
};
- target.ApplyTemplate();
+ target.UpdateChild();
Assert.IsType(target.Child);
var root = new TestRoot
@@ -64,5 +90,10 @@ namespace Perspex.Controls.UnitTests.Presenters
target.ApplyTemplate();
Assert.IsType(target.Child);
}
+
+ private class TestContentControl : TemplatedControl
+ {
+ public IControl Child { get; set; }
+ }
}
}
diff --git a/tests/Perspex.Controls.UnitTests/Presenters/ItemsPresenterTests.cs b/tests/Perspex.Controls.UnitTests/Presenters/ItemsPresenterTests.cs
index b5fc0a0869..dd7b41caee 100644
--- a/tests/Perspex.Controls.UnitTests/Presenters/ItemsPresenterTests.cs
+++ b/tests/Perspex.Controls.UnitTests/Presenters/ItemsPresenterTests.cs
@@ -3,6 +3,7 @@
using System.Collections.ObjectModel;
using System.Linq;
+using Moq;
using Perspex.Collections;
using Perspex.Controls.Generators;
using Perspex.Controls.Presenters;
@@ -15,6 +16,17 @@ namespace Perspex.Controls.UnitTests.Presenters
{
public class ItemsPresenterTests
{
+ [Fact]
+ public void Should_Register_With_Host_When_TemplatedParent_Set()
+ {
+ var host = new Mock();
+ var target = new ItemsPresenter();
+
+ target.SetValue(Control.TemplatedParentProperty, host.Object);
+
+ host.Verify(x => x.RegisterItemsPresenter(target));
+ }
+
[Fact]
public void Should_Add_Containers()
{
diff --git a/tests/Perspex.Controls.UnitTests/Presenters/ScrollContentPresenterTests.cs b/tests/Perspex.Controls.UnitTests/Presenters/ScrollContentPresenterTests.cs
index adb4bfd0fa..4f56587bb5 100644
--- a/tests/Perspex.Controls.UnitTests/Presenters/ScrollContentPresenterTests.cs
+++ b/tests/Perspex.Controls.UnitTests/Presenters/ScrollContentPresenterTests.cs
@@ -25,6 +25,7 @@ namespace Perspex.Controls.UnitTests.Presenters
},
};
+ target.UpdateChild();
target.Measure(new Size(100, 100));
target.Arrange(new Rect(0, 0, 100, 100));
@@ -43,6 +44,7 @@ namespace Perspex.Controls.UnitTests.Presenters
},
};
+ target.UpdateChild();
target.Measure(new Size(100, 100));
target.Arrange(new Rect(0, 0, 100, 100));
@@ -62,6 +64,7 @@ namespace Perspex.Controls.UnitTests.Presenters
},
};
+ target.UpdateChild();
target.Measure(new Size(100, 100));
target.Arrange(new Rect(0, 0, 100, 100));
@@ -81,6 +84,7 @@ namespace Perspex.Controls.UnitTests.Presenters
},
};
+ target.UpdateChild();
target.Measure(new Size(100, 100));
target.Arrange(new Rect(0, 0, 100, 100));
@@ -101,6 +105,7 @@ namespace Perspex.Controls.UnitTests.Presenters
},
};
+ target.UpdateChild();
target.Measure(new Size(100, 100));
target.Arrange(new Rect(0, 0, 100, 100));
@@ -116,6 +121,7 @@ namespace Perspex.Controls.UnitTests.Presenters
Content = content = new TestControl(),
};
+ target.UpdateChild();
target.Measure(new Size(100, 100));
target.Arrange(new Rect(0, 0, 100, 100));
@@ -136,6 +142,7 @@ namespace Perspex.Controls.UnitTests.Presenters
Offset = new Vector(25, 25),
};
+ target.UpdateChild();
target.Measure(new Size(100, 100));
target.Arrange(new Rect(0, 0, 100, 100));
@@ -152,6 +159,7 @@ namespace Perspex.Controls.UnitTests.Presenters
[ScrollContentPresenter.CanScrollHorizontallyProperty] = false,
};
+ target.UpdateChild();
target.Measure(new Size(100, 100));
Assert.Equal(new Size(100, double.PositiveInfinity), child.AvailableSize);
@@ -166,6 +174,7 @@ namespace Perspex.Controls.UnitTests.Presenters
Content = child,
};
+ target.UpdateChild();
target.Measure(new Size(100, 100));
Assert.Equal(Size.Infinity, child.AvailableSize);
@@ -181,6 +190,7 @@ namespace Perspex.Controls.UnitTests.Presenters
var set = new List();
+ target.UpdateChild();
target.Measure(new Size(100, 100));
target.GetObservable(ScrollViewer.ViewportProperty).Skip(1).Subscribe(_ => set.Add("Viewport"));
@@ -199,6 +209,7 @@ namespace Perspex.Controls.UnitTests.Presenters
Content = new Border { Width = 140, Height = 150 }
};
+ target.UpdateChild();
target.Measure(new Size(100, 100));
target.Arrange(new Rect(0, 0, 100, 100));
target.Offset = new Vector(10, 100);
@@ -232,7 +243,7 @@ namespace Perspex.Controls.UnitTests.Presenters
}
};
- target.ApplyTemplate();
+ target.UpdateChild();
target.Measure(Size.Infinity);
target.Arrange(new Rect(0, 0, 100, 100));
target.BringDescendentIntoView(target.Child, new Rect(200, 200, 0, 0));
@@ -259,7 +270,7 @@ namespace Perspex.Controls.UnitTests.Presenters
}
};
- target.ApplyTemplate();
+ target.UpdateChild();
target.Measure(Size.Infinity);
target.Arrange(new Rect(0, 0, 100, 100));
target.BringDescendentIntoView(border, new Rect(200, 200, 0, 0));
diff --git a/tests/Perspex.Controls.UnitTests/Presenters/ScrollContentPresenterTests_IScrollable.cs b/tests/Perspex.Controls.UnitTests/Presenters/ScrollContentPresenterTests_IScrollable.cs
index 0dc1c31462..22f024f2a3 100644
--- a/tests/Perspex.Controls.UnitTests/Presenters/ScrollContentPresenterTests_IScrollable.cs
+++ b/tests/Perspex.Controls.UnitTests/Presenters/ScrollContentPresenterTests_IScrollable.cs
@@ -20,6 +20,7 @@ namespace Perspex.Controls.UnitTests
Content = scrollable,
};
+ target.UpdateChild();
target.Measure(new Size(100, 100));
Assert.Equal(new Size(100, 100), scrollable.AvailableSize);
@@ -40,6 +41,7 @@ namespace Perspex.Controls.UnitTests
Content = scrollable,
};
+ target.UpdateChild();
target.Measure(new Size(100, 100));
target.Arrange(new Rect(0, 0, 100, 100));
@@ -56,6 +58,7 @@ namespace Perspex.Controls.UnitTests
var changed = false;
+ target.UpdateChild();
target.Measure(new Size(100, 100));
target.GetObservable(ScrollViewer.ViewportProperty).Skip(1).Subscribe(_ => changed = true);
@@ -75,7 +78,7 @@ namespace Perspex.Controls.UnitTests
Content = scrollable
};
- target.ApplyTemplate();
+ target.UpdateChild();
Assert.NotNull(scrollable.InvalidateScroll);
}
@@ -89,9 +92,9 @@ namespace Perspex.Controls.UnitTests
Content = scrollable
};
- target.ApplyTemplate();
+ target.UpdateChild();
target.Content = null;
- target.ApplyTemplate();
+ target.UpdateChild();
Assert.Null(scrollable.InvalidateScroll);
}
@@ -111,7 +114,7 @@ namespace Perspex.Controls.UnitTests
Content = scrollable
};
- target.ApplyTemplate();
+ target.UpdateChild();
Assert.Equal(scrollable.Extent, target.Extent);
Assert.Equal(scrollable.Offset, target.Offset);
@@ -140,8 +143,7 @@ namespace Perspex.Controls.UnitTests
Content = scrollable
};
- target.ApplyTemplate();
-
+ target.UpdateChild();
target.Offset = new Vector(25, 25);
Assert.Equal(target.Offset, scrollable.Offset);
diff --git a/tests/Perspex.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs b/tests/Perspex.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs
index c61c81e8ce..9416f4924f 100644
--- a/tests/Perspex.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs
+++ b/tests/Perspex.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs
@@ -6,7 +6,6 @@ using Perspex.Collections;
using Perspex.Controls.Presenters;
using Perspex.Controls.Primitives;
using Perspex.Controls.Templates;
-using Perspex.Input;
using Perspex.Interactivity;
using Xunit;
@@ -69,6 +68,7 @@ namespace Perspex.Controls.UnitTests.Primitives
};
target.ApplyTemplate();
+ target.Presenter.ApplyTemplate();
target.SelectedItem = items[1];
Assert.False(items[0].IsSelected);
@@ -92,6 +92,7 @@ namespace Perspex.Controls.UnitTests.Primitives
target.SelectedItem = items[1];
target.ApplyTemplate();
+ target.Presenter.ApplyTemplate();
Assert.False(items[0].IsSelected);
Assert.True(items[1].IsSelected);
@@ -114,6 +115,7 @@ namespace Perspex.Controls.UnitTests.Primitives
target.SelectedIndex = 1;
target.ApplyTemplate();
+ target.Presenter.ApplyTemplate();
Assert.False(items[0].IsSelected);
Assert.True(items[1].IsSelected);
@@ -241,6 +243,7 @@ namespace Perspex.Controls.UnitTests.Primitives
};
target.ApplyTemplate();
+ target.Presenter.ApplyTemplate();
items.Add(new Item { IsSelected = true });
Assert.Equal(2, target.SelectedIndex);
@@ -346,6 +349,7 @@ namespace Perspex.Controls.UnitTests.Primitives
};
target.ApplyTemplate();
+ target.Presenter.ApplyTemplate();
target.SelectedItem = items[1];
Assert.False(items[0].IsSelected);
@@ -376,6 +380,7 @@ namespace Perspex.Controls.UnitTests.Primitives
};
target.ApplyTemplate();
+ target.Presenter.ApplyTemplate();
target.SelectedItem = items[1];
Assert.False(items[0].IsSelected);
diff --git a/tests/Perspex.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs b/tests/Perspex.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs
index 3d19e353ad..8cb736d110 100644
--- a/tests/Perspex.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs
+++ b/tests/Perspex.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs
@@ -168,6 +168,7 @@ namespace Perspex.Controls.UnitTests.Primitives
};
target.ApplyTemplate();
+ target.Presenter.ApplyTemplate();
target.SelectedItems.Add(items[0]);
target.SelectedItems.Add(items[1]);
@@ -195,6 +196,7 @@ namespace Perspex.Controls.UnitTests.Primitives
};
target.ApplyTemplate();
+ target.Presenter.ApplyTemplate();
target.SelectedItems = new PerspexList