Skip to content

Commit

Permalink
fix(ItemsControl): ContentControl at the DataTemplate root should kee…
Browse files Browse the repository at this point in the history
…p bindings
  • Loading branch information
jeromelaban committed Mar 6, 2023
1 parent e86db9c commit 6542c27
Show file tree
Hide file tree
Showing 5 changed files with 212 additions and 3 deletions.
Expand Up @@ -480,6 +480,86 @@ public async Task When_ContentPresenter_ContainerRecycled_And_Explicit_Item()
Assert.IsNotNull(third.ContentTemplateSelector);
}

[TestMethod]
[RunsOnUIThread]
public async Task When_ContentPresenter_ContainerRecycled_And_ContentControl_Template()
{
var dataTemplate = (DataTemplate)XamlReader.Load(
"""
<DataTemplate xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'>
<ContentControl Content="{Binding}"/>
</DataTemplate>
""");

var selector = new TestTemplateSelector();

var source = new[]
{
"First",
"Second",
};

var SUT = new ItemsControl()
{
ItemsSource = source,
ItemTemplate = dataTemplate
};

WindowHelper.WindowContent = SUT;

await WindowHelper.WaitForIdle();

{
ContentPresenter first = null;
await WindowHelper.WaitFor(() => (first = SUT.ContainerFromItem(source[0]) as ContentPresenter) != null);

ContentPresenter second = null;
await WindowHelper.WaitFor(() => (second = SUT.ContainerFromItem(source[1]) as ContentPresenter) != null);

Assert.IsNotNull(first);
Assert.IsNotNull(second);

Assert.IsNotNull(first.Content);
Assert.IsNotNull(second.Content);

var firstInnerContent = first.ContentTemplateRoot as ContentControl;
Assert.AreEqual(source[0], firstInnerContent?.Content);
Assert.IsNotNull(firstInnerContent.GetBindingExpression(ContentControl.ContentProperty));

var secondInnerContent = second.ContentTemplateRoot as ContentControl;
Assert.AreEqual(source[1], secondInnerContent.Content);
Assert.IsNotNull(secondInnerContent.GetBindingExpression(ContentControl.ContentProperty));
}

SUT.ItemsSource = null;
await WindowHelper.WaitForIdle();

SUT.ItemsSource = source;
await WindowHelper.WaitForIdle();

{
ContentPresenter first = null;
await WindowHelper.WaitFor(() => (first = SUT.ContainerFromItem(source[0]) as ContentPresenter) != null);

ContentPresenter second = null;
await WindowHelper.WaitFor(() => (second = SUT.ContainerFromItem(source[1]) as ContentPresenter) != null);

Assert.IsNotNull(first);
Assert.IsNotNull(second);

Assert.IsNotNull(first.Content);
Assert.IsNotNull(second.Content);

var firstInnerContent = first.ContentTemplateRoot as ContentControl;
Assert.AreEqual(source[0], firstInnerContent?.Content);
Assert.IsNotNull(firstInnerContent.GetBindingExpression(ContentControl.ContentProperty));

var secondInnerContent = second.ContentTemplateRoot as ContentControl;
Assert.AreEqual(source[1], secondInnerContent.Content);
Assert.IsNotNull(secondInnerContent.GetBindingExpression(ContentControl.ContentProperty));
}
}

[TestMethod]
[RunsOnUIThread]
#if __MACOS__
Expand Down
@@ -0,0 +1,73 @@
using System;
using System.Linq;
using Windows.UI;
using System.Threading.Tasks;
using Private.Infrastructure;
using Uno.UI.RuntimeTests.Helpers;
using Windows.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TreeView = Microsoft.UI.Xaml.Controls.TreeView;
using TreeViewItem = Microsoft.UI.Xaml.Controls.TreeViewItem;
using Uno.UI.RuntimeTests.Tests.Windows_UI_Xaml_Controls.TreeViewTests;
using System.Collections.Generic;

namespace Uno.UI.RuntimeTests.Tests.Windows_UI_Xaml_Controls;

[TestClass]
[RunsOnUIThread]
public class Given_TreeView
{
// Test method that create a tree of three nested items from a itemssource and that will open and close a single treeview item twice and validate that the container is still containing the same property values
[TestMethod]
public async Task When_Open_Close_Twice()
{
var SUT = new When_Open_Close_Twice();
TestServices.WindowHelper.WindowContent = SUT;

var root = new MyNode();
root.Name = "root";
root.Children = new List<MyNode>();
var child1 = new MyNode { Name = "Child 1" };
var child2 = new MyNode { Name = "Child 2" };
root.Children.Add(child1);
root.Children.Add(child2);

SUT.myTree.ItemsSource = new[] { root };
await TestServices.WindowHelper.WaitForIdle();

var rootNode = (TreeViewItem)SUT.FindName("root");
rootNode.IsExpanded = true;
await TestServices.WindowHelper.WaitForIdle();

var child1Node = (TreeViewItem)SUT.FindName("Child 1");
Assert.IsNotNull(child1Node);
Assert.AreEqual("Child 1", child1Node.Content);

rootNode.IsExpanded = false;
await TestServices.WindowHelper.WaitForIdle();

rootNode.IsExpanded = true;
await TestServices.WindowHelper.WaitForIdle();

var child1NodeAfter = (TreeViewItem)SUT.FindName("Child 1");
Assert.IsNotNull(child1NodeAfter);

Assert.AreEqual("Child 1", child1NodeAfter.Content);

var child2NodeAfter = (TreeViewItem)SUT.FindName("Child 2");
Assert.IsNotNull(child2NodeAfter);

Assert.AreEqual("Child 2", child2NodeAfter.Content);
}

private class MyNode
{
public string Name { get; set; }
public List<MyNode> Children { get; set; }
}
}
@@ -0,0 +1,21 @@
<Grid x:Class="Uno.UI.RuntimeTests.Tests.Windows_UI_Xaml_Controls.TreeViewTests.When_Open_Close_Twice"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Uno.UI.RuntimeTests.Tests.Windows_UI_Xaml_Controls.TreeViewTests"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

<muxc:TreeView x:Name="myTree" x:FieldModifier="public" SelectionMode="Multiple"
ItemsSource="{Binding Children}">
<muxc:TreeView.ItemTemplate>
<DataTemplate>
<muxc:TreeViewItem ItemsSource="{Binding Children}"
Content="{Binding Name}"
Name="{Binding Name}" />
</DataTemplate>
</muxc:TreeView.ItemTemplate>
</muxc:TreeView>
</Grid>
@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
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;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

namespace Uno.UI.RuntimeTests.Tests.Windows_UI_Xaml_Controls.TreeViewTests;

public sealed partial class When_Open_Close_Twice : Grid
{
public When_Open_Close_Twice()
{
this.InitializeComponent();
}
}
17 changes: 14 additions & 3 deletions src/Uno.UI/UI/Xaml/Controls/ItemsControl/ItemsControl.cs
Expand Up @@ -1152,16 +1152,27 @@ internal void CleanUpContainer(global::Windows.UI.Xaml.DependencyObject element)

if (!isOwnContainer)
{
static void ClearPropertyWhenNoExpression(ContentControl target, DependencyProperty property)
{
// We must not clear the properties for the container if a binding expession
// is defined. This is a use-case present for TreeView, which generally uses TreeViewItem
// at the root of hierarchical templates.
if (target.GetBindingExpression(property) == null)
{
target.ClearValue(property);
}
}

// Clears value set in PrepareContainerForItemOverride
element.ClearValue(ContentControl.ContentProperty);
ClearPropertyWhenNoExpression(contentControl, ContentControl.ContentProperty);

if (contentControl.ContentTemplate is { } ct && ct == ItemTemplate)
{
contentControl.ClearValue(ContentControl.ContentTemplateProperty);
ClearPropertyWhenNoExpression(contentControl, ContentControl.ContentTemplateProperty);
}
else if (contentControl.ContentTemplateSelector is { } cts && cts == ItemTemplateSelector)
{
contentControl.ClearValue(ContentControl.ContentTemplateSelectorProperty);
ClearPropertyWhenNoExpression(contentControl, ContentControl.ContentTemplateSelectorProperty);
}
}
}
Expand Down

0 comments on commit 6542c27

Please sign in to comment.