Skip to content

Commit

Permalink
feat: Add support DataTemplate is container host
Browse files Browse the repository at this point in the history
  • Loading branch information
jeromelaban authored and davidjohnoliver committed Sep 1, 2020
1 parent 79a3444 commit 49c46f4
Show file tree
Hide file tree
Showing 6 changed files with 375 additions and 41 deletions.
2 changes: 1 addition & 1 deletion src/SamplesApp/SamplesApp.Skia/SamplesApp.Skia.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
<Import Project="..\SamplesApp.UnitTests.Shared\SamplesApp.UnitTests.Shared.projitems" Label="Shared" />

<ItemGroup>
<Content Update="@(Content)" CopyToOutputDirectory="Always" />
<Content Update="@(Content)" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,62 @@ public void When_ObservableVectorChanged()
source.Remove(1);
Assert.AreEqual(7, count);
}

[TestMethod]
public void When_ContainerStyleSet()
{
var count = 0;
var panel = new StackPanel();

var source = new ObservableVector<int>() { 1, 2, 3 };

var SUT = new ItemsControl()
{
ItemsPanelRoot = panel,
ItemContainerStyle = BuildBasicContainerStyle(),
InternalItemsPanelRoot = panel,
ItemTemplate = new DataTemplate(() =>
{
count++;
return new Border();
})
};

SUT.ApplyTemplate();

Assert.AreEqual(0, count);

SUT.ItemsSource = source;
Assert.AreEqual(3, count);

source.Add(4);
Assert.AreEqual(7, count);

source.Remove(1);
Assert.AreEqual(7, count);
}

private Style BuildBasicContainerStyle() =>
new Style(typeof(Windows.UI.Xaml.Controls.ListViewItem))
{
Setters = {
new Setter<ListViewItem>("Template", t =>
t.Template = Funcs.Create(() =>
new Grid
{
Children = {
new ContentPresenter()
.Apply(p => {
p.SetBinding(ContentPresenter.ContentTemplateProperty, new Binding(){ Path = "ContentTemplate", RelativeSource = RelativeSource.TemplatedParent });
p.SetBinding(ContentPresenter.ContentProperty, new Binding(){ Path = "Content", RelativeSource = RelativeSource.TemplatedParent });
})
}
}
)
)
}
};

}

public class MyItemsControl : ItemsControl
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -619,26 +619,156 @@ public void When_ItemClick()
Assert.AreEqual(0, selectionChanged[0].RemovedItems.Count);
}

[TestMethod]
public void When_ContainerSet_Then_ContentShouldBeSet()
{
var SUT = new ListView()
{
ItemsPanel = new ItemsPanelTemplate(() => new StackPanel()),
ItemContainerStyle = BuildBasicContainerStyle(),
ItemTemplate = new DataTemplate(() =>
{
var tb = new TextBlock();
tb.SetBinding(TextBlock.TextProperty, new Binding());
return tb;
}),
Template = new ControlTemplate(() => new ItemsPresenter()),
};

SUT.ForceLoaded();

var source = new[] {
"item 0",
};

SUT.ItemsSource = source;

Assert.AreEqual(-1, SUT.SelectedIndex);

var si = SUT.ContainerFromItem(source[0]) as SelectorItem;
Assert.IsNotNull(si);

var tb = si.ContentTemplateRoot as TextBlock;
Assert.AreEqual("item 0", tb?.Text);
}

[TestMethod]
public void When_IsItsOwnItemContainer_FromSource()
{
var SUT = new ListView()
{
Style = null,
ItemsPanel = new ItemsPanelTemplate(() => new StackPanel()),
ItemContainerStyle = BuildBasicContainerStyle(),
Template = new ControlTemplate(() => new ItemsPresenter()),
SelectionMode = ListViewSelectionMode.Single,
};

SUT.ForceLoaded();

var source = new[] {
new ListViewItem(){ Content = "item 1" },
new ListViewItem(){ Content = "item 2" },
new ListViewItem(){ Content = "item 3" },
new ListViewItem(){ Content = "item 4" },
};

SUT.ItemsSource = source;

var si = SUT.ContainerFromItem(source[0]) as ListViewItem;
Assert.IsNotNull(si);
Assert.AreEqual("item 1", si.Content);
}

[TestMethod]
public void When_NoItemTemplate()
{
var SUT = new ListView()
{
Style = null,
ItemsPanel = new ItemsPanelTemplate(() => new StackPanel()),
ItemContainerStyle = BuildBasicContainerStyle(),
ItemTemplate = null,
ItemTemplateSelector = null,
Template = new ControlTemplate(() => new ItemsPresenter()),
};

SUT.ForceLoaded();

var source = new[] {
"Item 1"
};

SUT.ItemsSource = source;

var si = SUT.ContainerFromItem(source[0]) as ListViewItem;
Assert.IsNotNull(si);
Assert.AreEqual("Item 1", si.Content);
Assert.IsInstanceOfType(si.ContentTemplateRoot, typeof(ImplicitTextBlock));
Assert.AreEqual("Item 1", (si.ContentTemplateRoot as TextBlock).Text);
}

[TestMethod]
public void When_IsItsOwnItemContainer_FromSource_With_DataTemplate()
{
var SUT = new ListView()
{
Style = null,
ItemsPanel = new ItemsPanelTemplate(() => new StackPanel()),
ItemContainerStyle = BuildBasicContainerStyle(),
ItemTemplate = new DataTemplate(() =>
{
var tb = new TextBlock();
tb.SetBinding(TextBlock.TextProperty, new Binding());
return tb;
}),
Template = new ControlTemplate(() => new ItemsPresenter()),
SelectionMode = ListViewSelectionMode.Single,
};

SUT.ForceLoaded();

var source = new object[] {
new ListViewItem(){ Content = "item 1" },
"item 2"
};

SUT.ItemsSource = source;

var si = SUT.ContainerFromItem(source[0]) as ListViewItem;
Assert.IsNotNull(si);
Assert.AreEqual("item 1", si.Content);
Assert.AreSame(si, source[0]);
Assert.IsFalse(si.IsGeneratedContainer);

var si2 = SUT.ContainerFromItem(source[1]) as ListViewItem;
Assert.IsNotNull(si2);
Assert.AreNotSame(si, source[1]);
Assert.AreEqual("item 2", si2.DataContext);
Assert.AreEqual("item 2", (si2.Content as TextBlock).Text);
Assert.IsTrue(si2.IsGeneratedContainer);
}

private Style BuildBasicContainerStyle() =>
new Style(typeof(Windows.UI.Xaml.Controls.ListViewItem))
{
Setters = {
new Setter<ListViewItem>("Template", t =>
t.Template = Funcs.Create(() =>
new Grid
{
Children = {
new ContentPresenter()
.Apply(p => {
p.SetBinding(ContentPresenter.ContentTemplateProperty, new Binding(){ Path = "ContentTemplate", RelativeSource = RelativeSource.TemplatedParent });
p.SetBinding(ContentPresenter.ContentProperty, new Binding(){ Path = "Content", RelativeSource = RelativeSource.TemplatedParent });
})
}
new Style(typeof(Windows.UI.Xaml.Controls.ListViewItem))
{
Setters = {
new Setter<ListViewItem>("Template", t =>
t.Template = Funcs.Create(() =>
new Grid
{
Children = {
new ContentPresenter()
.Apply(p => {
p.SetBinding(ContentPresenter.ContentTemplateProperty, new Binding(){ Path = "ContentTemplate", RelativeSource = RelativeSource.TemplatedParent });
p.SetBinding(ContentPresenter.ContentProperty, new Binding(){ Path = "Content", RelativeSource = RelativeSource.TemplatedParent });
})
}
)
}
)
}
};
)
}
};
}

public class MyModel
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Uno.Extensions;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;

namespace Uno.UI.Tests.ItemsControlTests_CustomContainer
{
[TestClass]
public class Given_ListViewBase_CustomContainer
{
[TestMethod]
public void When_TemplateRootIsOwnContainer()
{
var count = 0;
var panel = new StackPanel();

var SUT = new MyItemsControl()
{
ItemsPanelRoot = panel,
ItemTemplate = new DataTemplate(() =>
{
count++;
return new MyCustomItemContainer() { MyValue = 42 };
}),
Template = new ControlTemplate(() => new ItemsPresenter()),
};

SUT.ApplyTemplate();

SUT.ItemsSource = new object[]
{
42
};

var container = SUT.ContainerFromIndex(0) as MyCustomItemContainer;

Assert.IsNotNull(container);
Assert.IsTrue(container.IsGeneratedContainer);
Assert.IsFalse(container.ContentTemplateRoot is MyCustomItemContainer);
Assert.AreEqual(42, container.Content);
}

[TestMethod]
public void When_TemplateSelector_RootIsOwnContainer()
{
var count = 0;
var panel = new StackPanel();

var itemsPresenter = new MyItemsControl();

var itemTemplate = new DataTemplate(() =>
{
count++;
return new MyCustomItemContainer() { MyValue = 42 };
});

var SUT = new MyItemsControl()
{
ItemsPanelRoot = panel,
ItemTemplateSelector = new MyDataTemplateSelector(i => itemTemplate),
Template = new ControlTemplate(() => new ItemsPresenter()),
};

SUT.ApplyTemplate();

SUT.ItemsSource = new object[]
{
42
};

var container = SUT.ContainerFromIndex(0) as MyCustomItemContainer;

Assert.IsTrue(container is MyCustomItemContainer);
Assert.IsTrue(container.IsGeneratedContainer);
Assert.IsFalse(container.ContentTemplateRoot is MyCustomItemContainer);
Assert.AreEqual(42, container.Content);
}
}

public class MyDataTemplateSelector : DataTemplateSelector
{
private Func<object, DataTemplate> _selector;

public MyDataTemplateSelector(Func<object, DataTemplate> selector) => _selector = selector;

protected override DataTemplate SelectTemplateCore(object item) => _selector.Invoke(item);
}


public class MyItemsControl : ListView
{
protected override DependencyObject GetContainerForItemOverride()
=> new MyCustomItemContainer();

protected override bool IsItemItsOwnContainerOverride(object item)
=> item is MyCustomItemContainer;
}

public class MyCustomItemContainer : SelectorItem
{
public int MyValue
{
get { return (int)GetValue(MyValueProperty); }
set { SetValue(MyValueProperty, value); }
}

// Using a DependencyProperty as the backing store for MyValue. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MyValueProperty =
DependencyProperty.Register("MyValue", typeof(int), typeof(MyCustomItemContainer), new PropertyMetadata(0));


}
}
Original file line number Diff line number Diff line change
Expand Up @@ -602,7 +602,7 @@ protected virtual void OnContentChanged(object oldValue, object newValue)
}
}

private void TrySetDataContextFromContent(object value)
internal void TrySetDataContextFromContent(object value)
{
if (value != null)
{
Expand Down
Loading

0 comments on commit 49c46f4

Please sign in to comment.