Skip to content

Commit

Permalink
Merge pull request #1288 from unoplatform/dev/xygu/20241121/itemscont…
Browse files Browse the repository at this point in the history
…rols-itemspanelroot-fix

fix: workaround for tabbar&chip initial selection being discarded
  • Loading branch information
Xiaoy312 authored Nov 22, 2024
2 parents e81ca84 + 7230ecf commit 6ed77fc
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 2 deletions.
22 changes: 22 additions & 0 deletions src/Uno.Toolkit.RuntimeTests/Tests/ChipGroupTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -266,4 +266,26 @@ public async Task MultiToSingle_Selected_ShouldPreserveFirstSelection()
}

#endregion

#region Misc

[TestMethod]
public async Task Initial_Selection()
{
var setup = XamlHelper.LoadXaml<ChipGroup>("""
<utu:ChipGroup>
<utu:Chip Content="Uno" IsChecked="True"/>
<utu:Chip Content="Deux" />
<utu:Chip Content="Three" />
</utu:ChipGroup>
""");
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
}
28 changes: 26 additions & 2 deletions src/Uno.Toolkit.RuntimeTests/Tests/TabBarTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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))
};
}
Expand Down Expand Up @@ -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<TabBar>("""
<utu:TabBar>
<utu:TabBar.Items>
<utu:TabBarItem Content="Tab Uno" IsSelected="True"/>
<utu:TabBarItem Content="Tab Deux" />
<utu:TabBarItem Content="Tab Three" />
</utu:TabBar.Items>
</utu:TabBar>
""");
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;
Expand Down
8 changes: 8 additions & 0 deletions src/Uno.Toolkit.UI/Extensions/ItemsControlExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@ item as T ??
/// <remarks>An empty enumerable will returned if the <see cref="ItemsControl.ItemsPanelRoot"/> and the containers have not been materialized.</remarks>
public static IEnumerable<T> GetItemContainers<T>(this ItemsControl itemsControl) =>
itemsControl.ItemsPanelRoot?.Children.OfType<T>() ??
// #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<T>()
: null) ??
Enumerable.Empty<T>();

/// <summary>
Expand Down

0 comments on commit 6ed77fc

Please sign in to comment.