From 2ff2985a75b9e3e54a292c9dc4744eaa8d2b35d2 Mon Sep 17 00:00:00 2001 From: Jerome Laban Date: Wed, 19 Jul 2023 09:35:59 -0400 Subject: [PATCH] fix(listview): [Android] Adjust datacontext for header/footer for ListView --- .../Given_ListViewBase.cs | 90 +++++++++++++++++++ .../NativeListViewBaseAdapter.Android.cs | 24 +++-- 2 files changed, 109 insertions(+), 5 deletions(-) diff --git a/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Controls/Given_ListViewBase.cs b/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Controls/Given_ListViewBase.cs index c54bacfb45c0..3b4d78c03273 100644 --- a/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Controls/Given_ListViewBase.cs +++ b/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Controls/Given_ListViewBase.cs @@ -2933,6 +2933,96 @@ private sealed class AlwaysEqualClass : IEquatable [TestMethod] public async Task When_Items_Are_Equal_But_Different_References_FlipView() => await When_Items_Are_Equal_But_Different_References_Common(new FlipView()); + public record When_Header_DataContext_Model(string MyText); + + [TestMethod] + [RunsOnUIThread] + public async Task When_Header_DataContext() + { + TextBlock header = new TextBlock { Text = "empty" }; + TextBlock header2 = new TextBlock { Text = "empty" }; + + var SUT = new ListView() + { + ItemContainerStyle = BasicContainerStyle, + Header = new StackPanel + { + Background = new SolidColorBrush(Colors.Red), + Children = { + header, + header2, + } + } + }; + + header.SetBinding(TextBlock.TextProperty, new Binding { Path = new PropertyPath("MyText") }); + header2.SetBinding(TextBlock.TextProperty, new Binding { Path = new PropertyPath(".") }); + + WindowHelper.WindowContent = SUT; + await WindowHelper.WaitForIdle(); + + var source = new[] { + new ListViewItem(){ Content = "item 1" }, + }; + + SUT.ItemsSource = source; + await WindowHelper.WaitForIdle(); + + Assert.IsNull(header.DataContext); + + SUT.DataContext = new When_Header_DataContext_Model("test value"); + await WindowHelper.WaitForIdle(); + + Assert.AreEqual(SUT.DataContext, header.DataContext); + Assert.AreEqual("test value", header.Text); + Assert.AreEqual(SUT.DataContext, header2.DataContext); + Assert.AreEqual(header2.DataContext.ToString(), header2.Text); + } + + [RunsOnUIThread] + [TestMethod] + public async Task When_Footer_DataContext() + { + TextBlock header = new TextBlock { Text = "empty" }; + TextBlock header2 = new TextBlock { Text = "empty" }; + + var SUT = new ListView() + { + ItemContainerStyle = BasicContainerStyle, + Footer = new StackPanel + { + Background = new SolidColorBrush(Colors.Red), + Children = { + header, + header2, + } + } + }; + + header.SetBinding(TextBlock.TextProperty, new Binding { Path = new PropertyPath("MyText") }); + header2.SetBinding(TextBlock.TextProperty, new Binding { Path = new PropertyPath(".") }); + + WindowHelper.WindowContent = SUT; + await WindowHelper.WaitForIdle(); + + var source = new[] { + new ListViewItem(){ Content = "item 1" }, + }; + + SUT.ItemsSource = source; + await WindowHelper.WaitForIdle(); + + Assert.IsNull(header.DataContext); + + SUT.DataContext = new When_Header_DataContext_Model("test value"); + await WindowHelper.WaitForIdle(); + + Assert.AreEqual(SUT.DataContext, header.DataContext); + Assert.AreEqual("test value", header.Text); + Assert.AreEqual(SUT.DataContext, header2.DataContext); + Assert.AreEqual(header2.DataContext.ToString(), header2.Text); + } + private async Task When_Items_Are_Equal_But_Different_References_Common(Selector sut) { var obj1 = new AlwaysEqualClass(); diff --git a/src/Uno.UI/UI/Xaml/Controls/ListViewBase/NativeListViewBaseAdapter.Android.cs b/src/Uno.UI/UI/Xaml/Controls/ListViewBase/NativeListViewBaseAdapter.Android.cs index 213a76c063f3..bd60f69c857b 100644 --- a/src/Uno.UI/UI/Xaml/Controls/ListViewBase/NativeListViewBaseAdapter.Android.cs +++ b/src/Uno.UI/UI/Xaml/Controls/ListViewBase/NativeListViewBaseAdapter.Android.cs @@ -75,7 +75,9 @@ public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int positi } var isGroupHeader = parent.GetIsGroupHeader(position); - if (isGroupHeader || parent.GetIsHeader(position) || parent.GetIsFooter(position)) + var isHeader = parent.GetIsHeader(position); + var isFooter = parent.GetIsFooter(position); + if (isGroupHeader || isHeader || isFooter) { var item = parent.GetElementFromDisplayPosition(position); @@ -90,11 +92,23 @@ public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int positi var dataTemplate = GetDataTemplateFromItem(parent, item, viewType, isGroupHeader); - container.DataContext = item; - container.ContentTemplate = dataTemplate; - if (container.GetBindingExpression(ContentControl.ContentProperty) == null) + + if (!isHeader && !isFooter) + { + container.DataContext = item; + container.ContentTemplate = dataTemplate; + if (container.GetBindingExpression(ContentControl.ContentProperty) == null) + { + container.SetBinding(ContentControl.ContentProperty, new Binding()); + } + } + else { - container.SetBinding(ContentControl.ContentProperty, new Binding()); + // When showing the header/footer, the datacontext must be the listview's + // datacontext. We only need to se the content of the container, not its + // datacontext. + + container.Content = item; } } else if (viewType == IsOwnContainerType)