From 7230ecfd2ecf9f8497074146db4999c26d08d959 Mon Sep 17 00:00:00 2001 From: xiaoy312 Date: Thu, 21 Nov 2024 18:13:23 -0500 Subject: [PATCH] fix: workaround for tabbar&chip initial selection being discarded --- .../Tests/ChipGroupTests.cs | 22 +++++++++++++++ .../Tests/TabBarTests.cs | 28 +++++++++++++++++-- .../Extensions/ItemsControlExtensions.cs | 8 ++++++ 3 files changed, 56 insertions(+), 2 deletions(-) diff --git a/src/Uno.Toolkit.RuntimeTests/Tests/ChipGroupTests.cs b/src/Uno.Toolkit.RuntimeTests/Tests/ChipGroupTests.cs index 0bbc1f4ed..217c743cc 100644 --- a/src/Uno.Toolkit.RuntimeTests/Tests/ChipGroupTests.cs +++ b/src/Uno.Toolkit.RuntimeTests/Tests/ChipGroupTests.cs @@ -266,4 +266,26 @@ public async Task MultiToSingle_Selected_ShouldPreserveFirstSelection() } #endregion + + #region Misc + + [TestMethod] + public async Task Initial_Selection() + { + var setup = XamlHelper.LoadXaml(""" + + + + + + """); + await UnitTestUIContentHelperEx.SetContentAndWait(setup); + + var selected = setup.ContainerFromIndex(0) as Chip ?? throw new Exception("Container#0 not found"); + + Assert.AreEqual(selected, setup.SelectedItem, "SelectedItem is expected to be container#0"); + Assert.AreEqual(true, selected.IsChecked, "Container#0 should be selected"); + } + + #endregion } diff --git a/src/Uno.Toolkit.RuntimeTests/Tests/TabBarTests.cs b/src/Uno.Toolkit.RuntimeTests/Tests/TabBarTests.cs index 92f23bf9c..49556f0a9 100644 --- a/src/Uno.Toolkit.RuntimeTests/Tests/TabBarTests.cs +++ b/src/Uno.Toolkit.RuntimeTests/Tests/TabBarTests.cs @@ -34,7 +34,7 @@ namespace Uno.Toolkit.RuntimeTests.Tests { [TestClass] [RunsOnUIThread] - internal class TabBarTests + internal partial class TabBarTests // test cases { [TestMethod] [DataRow(new int[0], null)] @@ -278,7 +278,7 @@ double GetMinCalculatedDimen() return orientation switch { Orientation.Horizontal => padding[1] + padding[3] + tabBarItem.ActualHeight, - Orientation.Vertical => padding[0] + padding[2] + tabBarItem.ActualWidth, + Orientation.Vertical => padding[0] + padding[2] + tabBarItem.ActualWidth, _ => throw new ArgumentOutOfRangeException(nameof(orientation)) }; } @@ -500,7 +500,31 @@ public async Task Verify_ItemTemplate_Disabled_Not_Selectable() // Assert the second item is not selected Assert.AreNotSame(SUT.SelectedItem, source[1]); } + + [TestMethod] + public async Task Initial_Selection() + { + var setup = XamlHelper.LoadXaml(""" + + + + + + + + """); + await UnitTestUIContentHelperEx.SetContentAndWait(setup); + + var selected = setup.ContainerFromIndex(0) as TabBarItem ?? throw new Exception("Container#0 not found"); + Assert.AreEqual(0, setup.SelectedIndex, "SelectedIndex is expected to be 0"); + Assert.AreEqual(selected, setup.SelectedItem, "SelectedItem is expected to be container#0"); + Assert.AreEqual(true, selected.IsSelected, "Container#0 should be selected"); + } + } + + internal partial class TabBarTests // supporting classes/methods + { private class SelectedIndexTestViewModel : INotifyPropertyChanged { private int _p; diff --git a/src/Uno.Toolkit.UI/Extensions/ItemsControlExtensions.cs b/src/Uno.Toolkit.UI/Extensions/ItemsControlExtensions.cs index 5a206833b..2d3e67280 100644 --- a/src/Uno.Toolkit.UI/Extensions/ItemsControlExtensions.cs +++ b/src/Uno.Toolkit.UI/Extensions/ItemsControlExtensions.cs @@ -56,6 +56,14 @@ item as T ?? /// An empty enumerable will returned if the and the containers have not been materialized. public static IEnumerable GetItemContainers(this ItemsControl itemsControl) => itemsControl.ItemsPanelRoot?.Children.OfType() ?? + // #1281 workaround: ItemsPanelRoot would not be resolved until ItemsPanel is loaded, + // which will be the case between the ItemsControl::Loaded and ItemsPanel::Loaded. + // This is normally not a problem, as the container is typically created after that with ItemsSource. + // However, for xaml-defined items, this could cause a problem with initial selection synchronization, + // which happens on ItemsControl::Loaded. For this case, we will resort to using ItemsControl::Items. + (itemsControl.ItemsSource is null && itemsControl.Items is { } + ? itemsControl.Items.OfType() + : null) ?? Enumerable.Empty(); ///