Browse Source

Merge pull request #209 from Perspex/datacontext-fixes

Datacontext fixes
pull/213/head
Steven Kirk 11 years ago
parent
commit
bdd367c93e
  1. 9
      samples/TestApplication/GalleryStyle.cs
  2. 4
      src/Perspex.Controls/Generators/IItemContainerGenerator.cs
  3. 25
      src/Perspex.Controls/Generators/ItemContainerGenerator.cs
  4. 19
      src/Perspex.Controls/Generators/ItemContainerGenerator`1.cs
  5. 15
      src/Perspex.Controls/Generators/TreeItemContainerGenerator.cs
  6. 19
      src/Perspex.Controls/ItemsControl.cs
  7. 2
      src/Perspex.Controls/Perspex.Controls.csproj
  8. 2
      src/Perspex.Controls/Presenters/ContentPresenter.cs
  9. 17
      src/Perspex.Controls/Presenters/DeckPresenter.cs
  10. 20
      src/Perspex.Controls/Presenters/ItemsPresenter.cs
  11. 31
      src/Perspex.Controls/TabControl.cs
  12. 41
      src/Perspex.Controls/Templates/DataTemplateExtensions.cs
  13. 35
      src/Perspex.Controls/Templates/FuncMemberSelector.cs
  14. 18
      src/Perspex.Controls/Templates/IMemberSelector.cs
  15. 1
      src/Perspex.Themes.Default/DeckStyle.cs
  16. 1
      src/Perspex.Themes.Default/DropDownStyle.cs
  17. 1
      src/Perspex.Themes.Default/ItemsControlStyle.cs
  18. 1
      src/Perspex.Themes.Default/ListBoxStyle.cs
  19. 9
      src/Perspex.Themes.Default/TabControlStyle.cs
  20. 1
      src/Perspex.Themes.Default/TreeViewStyle.cs
  21. 24
      tests/Perspex.Controls.UnitTests/ContentControlTests.cs
  22. 66
      tests/Perspex.Controls.UnitTests/ItemsControlTests.cs
  23. 53
      tests/Perspex.Controls.UnitTests/Presenters/ItemsPresenterTests.cs
  24. 60
      tests/Perspex.Controls.UnitTests/TabControlTests.cs

9
samples/TestApplication/GalleryStyle.cs

@ -107,13 +107,10 @@ namespace TestApplication
new Deck new Deck
{ {
Name = "deck", Name = "deck",
DataTemplates = new DataTemplates MemberSelector = control.ContentSelector,
{
new DataTemplate<TabItem>(x => (Control)control.MaterializeDataTemplate(x.Content)),
},
[~Deck.TransitionProperty] = control[~TabControl.TransitionProperty], [~Deck.TransitionProperty] = control[~TabControl.TransitionProperty],
[!ItemsControl.ItemsProperty] = control[!ItemsControl.ItemsProperty], [!Deck.ItemsProperty] = control[!ItemsControl.ItemsProperty],
[!SelectingItemsControl.SelectedItemProperty] = control[!SelectingItemsControl.SelectedItemProperty], [!Deck.SelectedItemProperty] = control[!SelectingItemsControl.SelectedItemProperty],
[Grid.ColumnProperty] = 1, [Grid.ColumnProperty] = 1,
} }
} }

4
src/Perspex.Controls/Generators/IItemContainerGenerator.cs

@ -25,12 +25,12 @@ namespace Perspex.Controls.Generators
/// The index of the first item of the data in the containing collection. /// The index of the first item of the data in the containing collection.
/// </param> /// </param>
/// <param name="items">The items.</param> /// <param name="items">The items.</param>
/// <param name="itemTemplate">An optional item template.</param> /// <param name="selector">An optional member selector.</param>
/// <returns>The created controls.</returns> /// <returns>The created controls.</returns>
IList<IControl> CreateContainers( IList<IControl> CreateContainers(
int startingIndex, int startingIndex,
IEnumerable items, IEnumerable items,
IDataTemplate itemTemplate); IMemberSelector selector);
/// <summary> /// <summary>
/// Removes a set of created containers from the index and returns the removed controls. /// Removes a set of created containers from the index and returns the removed controls.

25
src/Perspex.Controls/Generators/ItemContainerGenerator.cs

@ -45,12 +45,12 @@ namespace Perspex.Controls.Generators
/// The index of the first item of the data in the containing collection. /// The index of the first item of the data in the containing collection.
/// </param> /// </param>
/// <param name="items">The items.</param> /// <param name="items">The items.</param>
/// <param name="itemTemplate">An optional item template.</param> /// <param name="selector">An optional member selector.</param>
/// <returns>The created container controls.</returns> /// <returns>The created container controls.</returns>
public IList<IControl> CreateContainers( public IList<IControl> CreateContainers(
int startingIndex, int startingIndex,
IEnumerable items, IEnumerable items,
IDataTemplate itemTemplate) IMemberSelector selector)
{ {
Contract.Requires<ArgumentNullException>(items != null); Contract.Requires<ArgumentNullException>(items != null);
@ -59,7 +59,8 @@ namespace Perspex.Controls.Generators
foreach (var item in items) foreach (var item in items)
{ {
IControl container = CreateContainer(item, itemTemplate); var i = selector != null ? selector.Select(item) : item;
var container = CreateContainer(i);
result.Add(container); result.Add(container);
} }
@ -141,24 +142,10 @@ namespace Perspex.Controls.Generators
/// Creates the container for an item. /// Creates the container for an item.
/// </summary> /// </summary>
/// <param name="item">The item.</param> /// <param name="item">The item.</param>
/// <param name="itemTemplate">An optional item template.</param>
/// <returns>The created container control.</returns> /// <returns>The created container control.</returns>
protected virtual IControl CreateContainer(object item, IDataTemplate itemTemplate) protected virtual IControl CreateContainer(object item)
{ {
if (item == null) return Owner.MaterializeDataTemplate(item);
{
return null;
}
else if (itemTemplate != null && itemTemplate.Match(item))
{
var result = itemTemplate.Build(item);
result.DataContext = item;
return result;
}
else
{
return Owner.MaterializeDataTemplate(item);
}
} }
/// <summary> /// <summary>

19
src/Perspex.Controls/Generators/ItemContainerGenerator`1.cs

@ -21,17 +21,24 @@ namespace Perspex.Controls.Generators
} }
/// <inheritdoc/> /// <inheritdoc/>
protected override IControl CreateContainer(object item, IDataTemplate itemTemplate) protected override IControl CreateContainer(object item)
{ {
T result = item as T; var container = item as T;
if (result == null) if (item == null)
{ {
result = new T(); return null;
}
else if (container != null)
{
return container;
}
else
{
var result = new T();
result.Content = Owner.MaterializeDataTemplate(item); result.Content = Owner.MaterializeDataTemplate(item);
return result;
} }
return result;
} }
} }
} }

15
src/Perspex.Controls/Generators/TreeItemContainerGenerator.cs

@ -46,9 +46,12 @@ namespace Perspex.Controls.Generators
/// The index of the first item of the data in the containing collection. /// The index of the first item of the data in the containing collection.
/// </param> /// </param>
/// <param name="items">The items.</param> /// <param name="items">The items.</param>
/// <param name="itemTemplate">An optional item template.</param> /// <param name="selector">An optional member selector.</param>
/// <returns>The created container controls.</returns> /// <returns>The created container controls.</returns>
public IList<IControl> CreateContainers(int startingIndex, IEnumerable items, IDataTemplate itemTemplate) public IList<IControl> CreateContainers(
int startingIndex,
IEnumerable items,
IMemberSelector selector)
{ {
Contract.Requires<ArgumentNullException>(items != null); Contract.Requires<ArgumentNullException>(items != null);
@ -57,8 +60,9 @@ namespace Perspex.Controls.Generators
foreach (var item in items) foreach (var item in items)
{ {
var container = CreateContainer(item, itemTemplate); var i = selector != null ? selector.Select(item) : item;
_containers.Add(item, container); var container = CreateContainer(i);
_containers.Add(i, container);
result.Add(container); result.Add(container);
} }
@ -156,9 +160,8 @@ namespace Perspex.Controls.Generators
/// Creates the container for an item. /// Creates the container for an item.
/// </summary> /// </summary>
/// <param name="item">The item.</param> /// <param name="item">The item.</param>
/// <param name="itemTemplate">An optional item template.</param>
/// <returns>The created container control.</returns> /// <returns>The created container control.</returns>
protected virtual T CreateContainer(object item, IDataTemplate itemTemplate) protected virtual T CreateContainer(object item)
{ {
T result = item as T; T result = item as T;

19
src/Perspex.Controls/ItemsControl.cs

@ -33,13 +33,19 @@ namespace Perspex.Controls
/// Defines the <see cref="Items"/> property. /// Defines the <see cref="Items"/> property.
/// </summary> /// </summary>
public static readonly PerspexProperty<IEnumerable> ItemsProperty = public static readonly PerspexProperty<IEnumerable> ItemsProperty =
PerspexProperty.Register<ItemsControl, IEnumerable>("Items"); PerspexProperty.Register<ItemsControl, IEnumerable>(nameof(Items));
/// <summary> /// <summary>
/// Defines the <see cref="ItemsPanel"/> property. /// Defines the <see cref="ItemsPanel"/> property.
/// </summary> /// </summary>
public static readonly PerspexProperty<ITemplate<IPanel>> ItemsPanelProperty = public static readonly PerspexProperty<ITemplate<IPanel>> ItemsPanelProperty =
PerspexProperty.Register<ItemsControl, ITemplate<IPanel>>("ItemsPanel", defaultValue: DefaultPanel); PerspexProperty.Register<ItemsControl, ITemplate<IPanel>>(nameof(ItemsPanel), DefaultPanel);
/// <summary>
/// Defines the <see cref="MemberSelector"/> property.
/// </summary>
public static readonly PerspexProperty<IMemberSelector> MemberSelectorProperty =
PerspexProperty.Register<ItemsControl, IMemberSelector>(nameof(MemberSelector));
private IItemContainerGenerator _itemContainerGenerator; private IItemContainerGenerator _itemContainerGenerator;
@ -94,6 +100,15 @@ namespace Perspex.Controls
set { SetValue(ItemsPanelProperty, value); } set { SetValue(ItemsPanelProperty, value); }
} }
/// <summary>
/// Selects a member from <see cref="Items"/> to use as the list item.
/// </summary>
public IMemberSelector MemberSelector
{
get { return GetValue(MemberSelectorProperty); }
set { SetValue(MemberSelectorProperty, value); }
}
/// <summary> /// <summary>
/// Gets the items presenter control. /// Gets the items presenter control.
/// </summary> /// </summary>

2
src/Perspex.Controls/Perspex.Controls.csproj

@ -71,6 +71,7 @@
<Compile Include="Canvas.cs" /> <Compile Include="Canvas.cs" />
<Compile Include="Templates\ControlTemplate`2.cs" /> <Compile Include="Templates\ControlTemplate`2.cs" />
<Compile Include="Templates\DataTemplate`1.cs" /> <Compile Include="Templates\DataTemplate`1.cs" />
<Compile Include="Templates\FuncMemberSelector.cs" />
<Compile Include="Templates\IControlTemplate.cs" /> <Compile Include="Templates\IControlTemplate.cs" />
<Compile Include="Templates\FuncTemplate`2.cs" /> <Compile Include="Templates\FuncTemplate`2.cs" />
<Compile Include="Templates\FuncTemplate`1.cs" /> <Compile Include="Templates\FuncTemplate`1.cs" />
@ -122,6 +123,7 @@
<Compile Include="Primitives\Thumb.cs" /> <Compile Include="Primitives\Thumb.cs" />
<Compile Include="RequestBringIntoViewEventArgs.cs" /> <Compile Include="RequestBringIntoViewEventArgs.cs" />
<Compile Include="Shapes\Ellipse.cs" /> <Compile Include="Shapes\Ellipse.cs" />
<Compile Include="Templates\IMemberSelector.cs" />
<Compile Include="Templates\ITemplate`2.cs" /> <Compile Include="Templates\ITemplate`2.cs" />
<Compile Include="Templates\ITemplate`1.cs" /> <Compile Include="Templates\ITemplate`1.cs" />
<Compile Include="Templates\ITreeDataTemplate.cs" /> <Compile Include="Templates\ITreeDataTemplate.cs" />

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

@ -97,7 +97,7 @@ namespace Perspex.Controls.Presenters
{ {
var old = Child; var old = Child;
var content = Content; var content = Content;
var result = content != null ? this.MaterializeDataTemplate(content) : null; var result = this.MaterializeDataTemplate(content);
var logicalHost = this.FindReparentingHost(); var logicalHost = this.FindReparentingHost();
var logicalChildren = logicalHost?.LogicalChildren ?? LogicalChildren; var logicalChildren = logicalHost?.LogicalChildren ?? LogicalChildren;

17
src/Perspex.Controls/Presenters/DeckPresenter.cs

@ -30,6 +30,12 @@ namespace Perspex.Controls.Presenters
/// </summary> /// </summary>
public static readonly PerspexProperty<ITemplate<IPanel>> ItemsPanelProperty = public static readonly PerspexProperty<ITemplate<IPanel>> ItemsPanelProperty =
ItemsControl.ItemsPanelProperty.AddOwner<DeckPresenter>(); ItemsControl.ItemsPanelProperty.AddOwner<DeckPresenter>();
/// <summary>
/// Defines the <see cref="MemberSelector"/> property.
/// </summary>
public static readonly PerspexProperty<IMemberSelector> MemberSelectorProperty =
ItemsControl.MemberSelectorProperty.AddOwner<DeckPresenter>();
/// <summary> /// <summary>
/// Defines the <see cref="SelectedIndex"/> property. /// Defines the <see cref="SelectedIndex"/> property.
@ -101,6 +107,15 @@ namespace Perspex.Controls.Presenters
set { SetValue(ItemsPanelProperty, value); } set { SetValue(ItemsPanelProperty, value); }
} }
/// <summary>
/// Selects a member from <see cref="Items"/> to use as the list item.
/// </summary>
public IMemberSelector MemberSelector
{
get { return GetValue(MemberSelectorProperty); }
set { SetValue(MemberSelectorProperty, value); }
}
/// <summary> /// <summary>
/// Gets or sets the index of the selected page. /// Gets or sets the index of the selected page.
/// </summary> /// </summary>
@ -181,7 +196,7 @@ namespace Perspex.Controls.Presenters
if (toIndex != -1) if (toIndex != -1)
{ {
var item = Items.Cast<object>().ElementAt(toIndex); var item = Items.Cast<object>().ElementAt(toIndex);
to = generator.CreateContainers(toIndex, new[] { item }, null).FirstOrDefault(); to = generator.CreateContainers(toIndex, new[] { item }, MemberSelector).FirstOrDefault();
if (to != null) if (to != null)
{ {

20
src/Perspex.Controls/Presenters/ItemsPresenter.cs

@ -28,6 +28,12 @@ namespace Perspex.Controls.Presenters
public static readonly PerspexProperty<ITemplate<IPanel>> ItemsPanelProperty = public static readonly PerspexProperty<ITemplate<IPanel>> ItemsPanelProperty =
ItemsControl.ItemsPanelProperty.AddOwner<ItemsPresenter>(); ItemsControl.ItemsPanelProperty.AddOwner<ItemsPresenter>();
/// <summary>
/// Defines the <see cref="MemberSelector"/> property.
/// </summary>
public static readonly PerspexProperty<IMemberSelector> MemberSelectorProperty =
ItemsControl.MemberSelectorProperty.AddOwner<ItemsPresenter>();
private bool _createdPanel; private bool _createdPanel;
private IItemContainerGenerator _generator; private IItemContainerGenerator _generator;
@ -96,6 +102,15 @@ namespace Perspex.Controls.Presenters
set { SetValue(ItemsPanelProperty, value); } set { SetValue(ItemsPanelProperty, value); }
} }
/// <summary>
/// Selects a member from <see cref="Items"/> to use as the list item.
/// </summary>
public IMemberSelector MemberSelector
{
get { return GetValue(MemberSelectorProperty); }
set { SetValue(MemberSelectorProperty, value); }
}
/// <summary> /// <summary>
/// Gets the panel used to display the items. /// Gets the panel used to display the items.
/// </summary> /// </summary>
@ -171,8 +186,7 @@ namespace Perspex.Controls.Presenters
{ {
if (items != null) if (items != null)
{ {
Panel.Children.AddRange( Panel.Children.AddRange(ItemContainerGenerator.CreateContainers(0, Items, MemberSelector));
ItemContainerGenerator.CreateContainers(0, Items, null));
INotifyCollectionChanged incc = items as INotifyCollectionChanged; INotifyCollectionChanged incc = items as INotifyCollectionChanged;
@ -229,7 +243,7 @@ namespace Perspex.Controls.Presenters
{ {
case NotifyCollectionChangedAction.Add: case NotifyCollectionChangedAction.Add:
Panel.Children.AddRange( Panel.Children.AddRange(
generator.CreateContainers(e.NewStartingIndex, e.NewItems, null)); generator.CreateContainers(e.NewStartingIndex, e.NewItems, MemberSelector));
break; break;
case NotifyCollectionChangedAction.Remove: case NotifyCollectionChangedAction.Remove:

31
src/Perspex.Controls/TabControl.cs

@ -4,6 +4,7 @@
using Perspex.Animation; using Perspex.Animation;
using Perspex.Controls.Presenters; using Perspex.Controls.Presenters;
using Perspex.Controls.Primitives; using Perspex.Controls.Primitives;
using Perspex.Controls.Templates;
namespace Perspex.Controls namespace Perspex.Controls
{ {
@ -24,6 +25,9 @@ namespace Perspex.Controls
public static readonly PerspexProperty<IPageTransition> TransitionProperty = public static readonly PerspexProperty<IPageTransition> TransitionProperty =
Deck.TransitionProperty.AddOwner<TabControl>(); Deck.TransitionProperty.AddOwner<TabControl>();
private static readonly IMemberSelector s_contentSelector =
new FuncMemberSelector<object, object>(SelectContent);
/// <summary> /// <summary>
/// Initializes static members of the <see cref="TabControl"/> class. /// Initializes static members of the <see cref="TabControl"/> class.
/// </summary> /// </summary>
@ -34,6 +38,14 @@ namespace Perspex.Controls
SelectedIndexProperty.Changed.AddClassHandler<TabControl>(x => x.SelectedIndexChanged); SelectedIndexProperty.Changed.AddClassHandler<TabControl>(x => x.SelectedIndexChanged);
} }
/// <summary>
/// Gets an <see cref="IMemberSelector"/> that selects the content of a <see cref="TabItem"/>.
/// </summary>
public IMemberSelector ContentSelector
{
get { return s_contentSelector; }
}
/// <summary> /// <summary>
/// Gets the <see cref="SelectingItemsControl.SelectedItem"/> as a <see cref="TabItem"/>. /// Gets the <see cref="SelectingItemsControl.SelectedItem"/> as a <see cref="TabItem"/>.
/// </summary> /// </summary>
@ -65,6 +77,25 @@ namespace Perspex.Controls
return control is DeckPresenter; return control is DeckPresenter;
} }
/// <summary>
/// Selects the content of a tab item.
/// </summary>
/// <param name="o">The tab item.</param>
/// <returns>The content.</returns>
private static object SelectContent(object o)
{
var content = o as IContentControl;
if (content != null)
{
return content.Content;
}
else
{
return o;
}
}
/// <summary> /// <summary>
/// Called when the <see cref="SelectingItemsControl.SelectedIndex"/> property changes. /// Called when the <see cref="SelectingItemsControl.SelectedIndex"/> property changes.
/// </summary> /// </summary>

41
src/Perspex.Controls/Templates/DataTemplateExtensions.cs

@ -19,28 +19,37 @@ namespace Perspex.Controls.Templates
/// <returns>The data materialized as a control.</returns> /// <returns>The data materialized as a control.</returns>
public static IControl MaterializeDataTemplate(this IControl control, object data) public static IControl MaterializeDataTemplate(this IControl control, object data)
{ {
IDataTemplate template = control.FindDataTemplate(data); if (data == null)
IControl result;
if (template != null)
{ {
result = template.Build(data); return null;
}
else
{
var asControl = data as IControl;
if (result != null && result.DataContext == null) if (asControl != null)
{
return asControl;
}
else
{ {
IDataTemplate template = control.FindDataTemplate(data);
IControl result;
if (template != null)
{
result = template.Build(data);
}
else
{
result = DataTemplate.Default.Build(data);
}
result.DataContext = data; result.DataContext = data;
return result;
} }
} }
else if (data is IControl)
{
result = (IControl)data;
}
else
{
result = DataTemplate.Default.Build(data);
}
return result;
} }
/// <summary> /// <summary>

35
src/Perspex.Controls/Templates/FuncMemberSelector.cs

@ -0,0 +1,35 @@
// 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;
namespace Perspex.Controls.Templates
{
/// <summary>
/// Selects a member of an object using a <see cref="Func{TObject, TMember}"/>.
/// </summary>
public class FuncMemberSelector<TObject, TMember> : IMemberSelector
{
private Func<TObject, TMember> _selector;
/// <summary>
/// Initializes a new instance of the <see cref="FuncMemberSelector{TObject, TMember}"/>
/// class.
/// </summary>
/// <param name="selector">The selector.</param>
public FuncMemberSelector(Func<TObject, TMember> selector)
{
this._selector = selector;
}
/// <summary>
/// Selects a member of an object.
/// </summary>
/// <param name="o">The obeject.</param>
/// <returns>The selected member.</returns>
public object Select(object o)
{
return (o is TObject) ? _selector((TObject)o) : default(TMember);
}
}
}

18
src/Perspex.Controls/Templates/IMemberSelector.cs

@ -0,0 +1,18 @@
// 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.Controls.Templates
{
/// <summary>
/// Selects a member of an object.
/// </summary>
public interface IMemberSelector
{
/// <summary>
/// Selects a member of an object.
/// </summary>
/// <param name="o">The obeject.</param>
/// <returns>The selected member.</returns>
object Select(object o);
}
}

1
src/Perspex.Themes.Default/DeckStyle.cs

@ -42,6 +42,7 @@ namespace Perspex.Themes.Default
return new DeckPresenter return new DeckPresenter
{ {
Name = "itemsPresenter", Name = "itemsPresenter",
MemberSelector = control.MemberSelector,
[~ItemsPresenter.ItemsProperty] = control[~ItemsControl.ItemsProperty], [~ItemsPresenter.ItemsProperty] = control[~ItemsControl.ItemsProperty],
[~ItemsPresenter.ItemsPanelProperty] = control[~ItemsControl.ItemsPanelProperty], [~ItemsPresenter.ItemsPanelProperty] = control[~ItemsControl.ItemsPanelProperty],
[~DeckPresenter.SelectedIndexProperty] = control[~SelectingItemsControl.SelectedIndexProperty], [~DeckPresenter.SelectedIndexProperty] = control[~SelectingItemsControl.SelectedIndexProperty],

1
src/Perspex.Themes.Default/DropDownStyle.cs

@ -128,6 +128,7 @@ namespace Perspex.Themes.Default
Padding = new Thickness(4), Padding = new Thickness(4),
Child = new ItemsPresenter Child = new ItemsPresenter
{ {
MemberSelector = control.MemberSelector,
[~ItemsPresenter.ItemsProperty] = control[~ItemsControl.ItemsProperty], [~ItemsPresenter.ItemsProperty] = control[~ItemsControl.ItemsProperty],
} }
}, },

1
src/Perspex.Themes.Default/ItemsControlStyle.cs

@ -42,6 +42,7 @@ namespace Perspex.Themes.Default
return new ItemsPresenter return new ItemsPresenter
{ {
Name = "itemsPresenter", Name = "itemsPresenter",
MemberSelector = control.MemberSelector,
[~ItemsPresenter.ItemsProperty] = control[~ItemsControl.ItemsProperty], [~ItemsPresenter.ItemsProperty] = control[~ItemsControl.ItemsProperty],
[~ItemsPresenter.ItemsPanelProperty] = control[~ItemsControl.ItemsPanelProperty], [~ItemsPresenter.ItemsPanelProperty] = control[~ItemsControl.ItemsPanelProperty],
}; };

1
src/Perspex.Themes.Default/ListBoxStyle.cs

@ -53,6 +53,7 @@ namespace Perspex.Themes.Default
Content = new ItemsPresenter Content = new ItemsPresenter
{ {
Name = "itemsPresenter", Name = "itemsPresenter",
MemberSelector = control.MemberSelector,
[~ItemsPresenter.ItemsProperty] = control[~ItemsControl.ItemsProperty], [~ItemsPresenter.ItemsProperty] = control[~ItemsControl.ItemsProperty],
[~ItemsPresenter.ItemsPanelProperty] = control[~ItemsControl.ItemsPanelProperty], [~ItemsPresenter.ItemsPanelProperty] = control[~ItemsControl.ItemsPanelProperty],
} }

9
src/Perspex.Themes.Default/TabControlStyle.cs

@ -59,12 +59,9 @@ namespace Perspex.Themes.Default
new Deck new Deck
{ {
Name = "deck", Name = "deck",
DataTemplates = new DataTemplates MemberSelector = new FuncMemberSelector<TabItem, object>(x => x.Content),
{ [!Deck.ItemsProperty] = control[!ItemsControl.ItemsProperty],
new DataTemplate<TabItem>(x => (Control)control.MaterializeDataTemplate(x.Content)), [!Deck.SelectedItemProperty] = control[!SelectingItemsControl.SelectedItemProperty],
},
[!ItemsControl.ItemsProperty] = control[!ItemsControl.ItemsProperty],
[!SelectingItemsControl.SelectedItemProperty] = control[!SelectingItemsControl.SelectedItemProperty],
[~Deck.TransitionProperty] = control[~TabControl.TransitionProperty], [~Deck.TransitionProperty] = control[~TabControl.TransitionProperty],
[Grid.RowProperty] = 1, [Grid.RowProperty] = 1,
} }

1
src/Perspex.Themes.Default/TreeViewStyle.cs

@ -54,6 +54,7 @@ namespace Perspex.Themes.Default
Content = new ItemsPresenter Content = new ItemsPresenter
{ {
Name = "itemsPresenter", Name = "itemsPresenter",
MemberSelector = control.MemberSelector,
[~ItemsPresenter.ItemsProperty] = control[~ItemsControl.ItemsProperty], [~ItemsPresenter.ItemsProperty] = control[~ItemsControl.ItemsProperty],
[~ItemsPresenter.ItemsPanelProperty] = control[~ItemsControl.ItemsPanelProperty], [~ItemsPresenter.ItemsPanelProperty] = control[~ItemsControl.ItemsPanelProperty],
} }

24
tests/Perspex.Controls.UnitTests/ContentControlTests.cs

@ -262,6 +262,30 @@ namespace Perspex.Controls.UnitTests
Assert.Equal("Bar", ((TextBlock)target.Presenter.Child).Text); Assert.Equal("Bar", ((TextBlock)target.Presenter.Child).Text);
} }
[Fact]
public void DataContext_Should_Be_Set_For_Templated_Data()
{
var target = new ContentControl();
target.Template = GetTemplate();
target.Content = "Foo";
target.ApplyTemplate();
Assert.Equal("Foo", target.Presenter.Child.DataContext);
}
[Fact]
public void DataContext_Should_Not_Be_Set_For_Control_Data()
{
var target = new ContentControl();
target.Template = GetTemplate();
target.Content = new TextBlock();
target.ApplyTemplate();
Assert.Null(target.Presenter.Child.DataContext);
}
private ControlTemplate GetTemplate() private ControlTemplate GetTemplate()
{ {
return new ControlTemplate<ContentControl>(parent => return new ControlTemplate<ContentControl>(parent =>

66
tests/Perspex.Controls.UnitTests/ItemsControlTests.cs

@ -6,6 +6,7 @@ using System.Linq;
using Perspex.Collections; using Perspex.Collections;
using Perspex.Controls.Presenters; using Perspex.Controls.Presenters;
using Perspex.Controls.Templates; using Perspex.Controls.Templates;
using Perspex.LogicalTree;
using Perspex.Styling; using Perspex.Styling;
using Perspex.VisualTree; using Perspex.VisualTree;
using Xunit; using Xunit;
@ -287,6 +288,70 @@ namespace Perspex.Controls.UnitTests
Assert.Equal(target, ((ILogical)child).LogicalParent); Assert.Equal(target, ((ILogical)child).LogicalParent);
} }
[Fact]
public void DataContexts_Should_Be_Correctly_Set()
{
var items = new object[]
{
"Foo",
new Item("Bar"),
new TextBlock { Text = "Baz" },
new ListBoxItem { Content = "Qux" },
};
var target = new ItemsControl
{
Template = GetTemplate(),
DataContext = "Base",
DataTemplates = new DataTemplates
{
new DataTemplate<Item>(x => new Button { Content = x })
},
Items = items,
};
target.ApplyTemplate();
var dataContexts = target.Presenter.Panel.Children
.Cast<Control>()
.Select(x => x.DataContext)
.ToList();
Assert.Equal(
new object[] { items[0], items[1], "Base", "Base" },
dataContexts);
}
[Fact]
public void MemberSelector_Should_Select_Member()
{
var target = new ItemsControl
{
Template = GetTemplate(),
Items = new[] { new Item("Foo"), new Item("Bar") },
MemberSelector = new FuncMemberSelector<Item, string>(x => x.Value),
};
target.ApplyTemplate();
var text = target.Presenter.Panel.Children
.Cast<TextBlock>()
.Select(x => x.Text)
.ToList();
Assert.Equal(new[] { "Foo", "Bar" }, text);
}
private class Item
{
public Item(string value)
{
Value = value;
}
public string Value { get; }
}
private ControlTemplate GetTemplate() private ControlTemplate GetTemplate()
{ {
return new ControlTemplate<ItemsControl>(parent => return new ControlTemplate<ItemsControl>(parent =>
@ -297,6 +362,7 @@ namespace Perspex.Controls.UnitTests
Child = new ItemsPresenter Child = new ItemsPresenter
{ {
Name = "itemsPresenter", Name = "itemsPresenter",
MemberSelector = parent.MemberSelector,
[~ItemsPresenter.ItemsProperty] = parent[~ItemsControl.ItemsProperty], [~ItemsPresenter.ItemsProperty] = parent[~ItemsControl.ItemsProperty],
} }
}; };

53
tests/Perspex.Controls.UnitTests/Presenters/ItemsPresenterTests.cs

@ -101,12 +101,12 @@ namespace Perspex.Controls.UnitTests.Presenters
target.ApplyTemplate(); target.ApplyTemplate();
var text = target.Panel.Children.OfType<TextBlock>().Select(x => x.Text).ToList(); var text = target.Panel.Children.Cast<TextBlock>().Select(x => x.Text).ToList();
Assert.Equal(new[] { "foo", "bar" }, text); Assert.Equal(new[] { "foo", "bar" }, text);
items.RemoveAt(1); items.RemoveAt(1);
text = target.Panel.Children.OfType<TextBlock>().Select(x => x.Text).ToList(); text = target.Panel.Children.Cast<TextBlock>().Select(x => x.Text).ToList();
Assert.Equal(new[] { "foo", "bar" }, text); Assert.Equal(new[] { "foo", "bar" }, text);
} }
@ -174,6 +174,55 @@ namespace Perspex.Controls.UnitTests.Presenters
Assert.Equal(target.Panel, child); Assert.Equal(target.Panel, child);
} }
[Fact]
public void MemberSelector_Should_Select_Member()
{
var target = new ItemsPresenter
{
Items = new[] { new Item("Foo"), new Item("Bar") },
MemberSelector = new FuncMemberSelector<Item, string>(x => x.Value),
};
target.ApplyTemplate();
var text = target.Panel.Children
.Cast<TextBlock>()
.Select(x => x.Text)
.ToList();
Assert.Equal(new[] { "Foo", "Bar" }, text);
}
[Fact]
public void MemberSelector_Should_Set_DataContext()
{
var items = new[] { new Item("Foo"), new Item("Bar") };
var target = new ItemsPresenter
{
Items = items,
MemberSelector = new FuncMemberSelector<Item, string>(x => x.Value),
};
target.ApplyTemplate();
var dataContexts = target.Panel.Children
.Cast<TextBlock>()
.Select(x => x.DataContext)
.ToList();
Assert.Equal(new[] { "Foo", "Bar" }, dataContexts);
}
private class Item
{
public Item(string value)
{
Value = value;
}
public string Value { get; }
}
private class TestItem : ContentControl private class TestItem : ContentControl
{ {
} }

60
tests/Perspex.Controls.UnitTests/TabControlTests.cs

@ -130,6 +130,46 @@ namespace Perspex.Controls.UnitTests
Assert.Same(target.SelectedTab, target.SelectedItem); Assert.Same(target.SelectedTab, target.SelectedItem);
} }
[Fact]
public void DataContexts_Should_Be_Correctly_Set()
{
var items = new object[]
{
"Foo",
new Item("Bar"),
new TextBlock { Text = "Baz" },
new TabItem { Content = "Qux" },
};
var target = new TabControl
{
Template = new ControlTemplate<TabControl>(CreateTabControlTemplate),
DataContext = "Base",
DataTemplates = new DataTemplates
{
new DataTemplate<Item>(x => new Button { Content = x })
},
Items = items,
};
target.ApplyTemplate();
var dataContext = ((TextBlock)target.GetLogicalChildren().Single()).DataContext;
Assert.Equal(items[0], dataContext);
target.SelectedIndex = 1;
dataContext = ((Button)target.GetLogicalChildren().Single()).DataContext;
Assert.Equal(items[1], dataContext);
target.SelectedIndex = 2;
dataContext = ((TextBlock)target.GetLogicalChildren().Single()).DataContext;
Assert.Equal("Base", dataContext);
target.SelectedIndex = 3;
dataContext = ((TextBlock)target.GetLogicalChildren().Single()).DataContext;
Assert.Equal("Qux", dataContext);
}
private Control CreateTabControlTemplate(TabControl parent) private Control CreateTabControlTemplate(TabControl parent)
{ {
return new StackPanel return new StackPanel
@ -147,10 +187,7 @@ namespace Perspex.Controls.UnitTests
{ {
Name = "deck", Name = "deck",
Template = new ControlTemplate<Deck>(CreateDeckTemplate), Template = new ControlTemplate<Deck>(CreateDeckTemplate),
DataTemplates = new DataTemplates MemberSelector = parent.ContentSelector,
{
new DataTemplate<TabItem>(x => (Control)parent.MaterializeDataTemplate(x.Content)),
},
[!ItemsControl.ItemsProperty] = parent[!ItemsControl.ItemsProperty], [!ItemsControl.ItemsProperty] = parent[!ItemsControl.ItemsProperty],
[!SelectingItemsControl.SelectedItemProperty] = parent[!SelectingItemsControl.SelectedItemProperty], [!SelectingItemsControl.SelectedItemProperty] = parent[!SelectingItemsControl.SelectedItemProperty],
} }
@ -172,11 +209,22 @@ namespace Perspex.Controls.UnitTests
return new DeckPresenter return new DeckPresenter
{ {
Name = "itemsPresenter", Name = "itemsPresenter",
[!ItemsPresenter.ItemsProperty] = control[!ItemsControl.ItemsProperty], [!DeckPresenter.ItemsProperty] = control[!ItemsControl.ItemsProperty],
[!ItemsPresenter.ItemsPanelProperty] = control[!ItemsControl.ItemsPanelProperty], [!DeckPresenter.ItemsPanelProperty] = control[!ItemsControl.ItemsPanelProperty],
[!DeckPresenter.MemberSelectorProperty] = control[!ItemsControl.MemberSelectorProperty],
[!DeckPresenter.SelectedIndexProperty] = control[!SelectingItemsControl.SelectedIndexProperty], [!DeckPresenter.SelectedIndexProperty] = control[!SelectingItemsControl.SelectedIndexProperty],
[~DeckPresenter.TransitionProperty] = control[~Deck.TransitionProperty], [~DeckPresenter.TransitionProperty] = control[~Deck.TransitionProperty],
}; };
} }
private class Item
{
public Item(string value)
{
Value = value;
}
public string Value { get; }
}
} }
} }

Loading…
Cancel
Save