Skip to content

Commit

Permalink
Merge pull request #18542 from unoplatform/mergify/bp/release/stable/…
Browse files Browse the repository at this point in the history
…5.5/pr-18492

fix(ios): Tabview Selection when Bindings items (backport #18492)
  • Loading branch information
jeromelaban authored Oct 22, 2024
2 parents d5e4b5a + 24093b4 commit 5384cc4
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -114,4 +114,107 @@ public async Task When_Tab_Removed()
Assert.AreEqual(0, SUT.SelectedIndex);
Assert.AreEqual("Tab 2", ((TabViewItem)SUT.TabItems[0]).Header);
}

[TestMethod]
public async Task When_SelectedItem_Changed()
{
var SUT = new TabView
{
TabItems =
{
new TabViewItem { Header = "Tab 1" },
new TabViewItem { Header = "Tab 2" }
}
};

await UITestHelper.Load(SUT);

Assert.AreEqual(0, SUT.SelectedIndex);

SUT.SelectedItem = SUT.TabItems[1];

await WindowHelper.WaitForIdle();

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

[TestMethod]
public async Task When_DataBinding()
{
var vm = new ViewModel();
var SUT = new TabView();

SUT.SetBinding(TabView.TabItemsSourceProperty, new Microsoft.UI.Xaml.Data.Binding() { Path = new("TabItems") });
SUT.SetBinding(TabView.SelectedItemProperty, new Microsoft.UI.Xaml.Data.Binding() { Path = new("SelectedItem"), Mode = Microsoft.UI.Xaml.Data.BindingMode.TwoWay });

SUT.DataContext = vm;

await UITestHelper.Load(SUT);

Assert.AreEqual(2, SUT.TabItems.Count);
Assert.AreEqual(0, SUT.SelectedIndex);
}

[TestMethod]
public async Task When_SelectedItem_Changed_Binding()
{
var vm = new ViewModel();
var SUT = new TabView();

SUT.SetBinding(TabView.TabItemsSourceProperty, new Microsoft.UI.Xaml.Data.Binding() { Path = new("TabItems") });
SUT.SetBinding(TabView.SelectedItemProperty, new Microsoft.UI.Xaml.Data.Binding() { Path = new("SelectedItem"), Mode = Microsoft.UI.Xaml.Data.BindingMode.TwoWay });

SUT.DataContext = vm;

vm.SelectedItem = vm.TabItems[1];

await UITestHelper.Load(SUT);

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

[TestMethod]
public async Task When_AddingTab_While_Binding()
{
var SUT = new TabView();

SUT.SetBinding(TabView.TabItemsSourceProperty, new Microsoft.UI.Xaml.Data.Binding() { Path = new("TabItems") });
SUT.SetBinding(TabView.SelectedItemProperty, new Microsoft.UI.Xaml.Data.Binding() { Path = new("SelectedItem"), Mode = Microsoft.UI.Xaml.Data.BindingMode.TwoWay });

SUT.DataContext = new ViewModel(addTab: true);

await UITestHelper.Load(SUT);

// It should select the newly added tab
Assert.AreEqual(2, SUT.SelectedIndex);

var tabviewItem = SUT.ContainerFromItem(SUT.SelectedItem) as TabViewItem;
Assert.IsTrue(tabviewItem.ActualWidth > 0, "TabViewItem should have a non-zero width.");
Assert.IsTrue(tabviewItem.ActualHeight > 0, "TabViewItem should have a non-zero height.");
}
}

public class ViewModel : ViewModelBase
{
public ViewModel(bool addTab = false)
{
if (addTab)
{
TabItems.Add(new TabViewItem { Header = "Main Tab", Content = "Main Content" });
SelectedItem = TabItems[^1];
}
}
private TabViewItem _selectedItem;

public TabViewItem SelectedItem
{
get => _selectedItem;
set => SetAndRaiseIfChanged(ref _selectedItem, value);
}

public ObservableCollection<TabViewItem> TabItems { get; } = new ObservableCollection<TabViewItem>
{
new TabViewItem { Header = "Tab 1", Content = "Content 1" },
new TabViewItem { Header = "Tab 2", Content = "Content 2" }
};
}
22 changes: 7 additions & 15 deletions src/Uno.UI/Microsoft/UI/Xaml/Controls/TabView/TabView.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.
// MUX Reference TabView.cpp, commit ed31e13

Expand Down Expand Up @@ -551,7 +551,9 @@ private void OnListViewLoaded(object sender, RoutedEventArgs args)
TabItems = lvItems;
}

if (ReadLocalValue(SelectedItemProperty) != DependencyProperty.UnsetValue)
// ReadLocalValue() is not returning UnsetValue even though SelectedItem is not yet set
if (ReadLocalValue(SelectedItemProperty) != DependencyProperty.UnsetValue
&& SelectedItem is not null)
{
UpdateSelectedItem();
}
Expand Down Expand Up @@ -1268,27 +1270,17 @@ public object ItemFromContainer(DependencyObject container)

private int GetItemCount()
{
var itemsSource = TabItemsSource;
if (itemsSource != null)
if (TabItemsSource is { } itemsSource)
{
var iterable = itemsSource as IEnumerable;
if (iterable != null)
if (itemsSource is IEnumerable iterable)
{
//int i = 1;
//var iter = iterable.First();
//while (iter.MoveNext())
//{
// i++;
//}
//return i;
return iterable.Count();
}
return 0;
}

else
{
return (int)TabItems.Count;
return TabItems.Count;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,10 @@ private void ScrollIntoViewFastPath(UIElement element, ScrollIntoViewAlignment a
#endif
);

var orientation = ItemsPanelRoot?.PhysicalOrientation ?? Orientation.Vertical;

var (elementOffset, elementLength, presenterOffset, presenterViewportLength) =
ItemsPanelRoot.PhysicalOrientation is Orientation.Vertical
orientation is Orientation.Vertical
? (offsetXY.Y, element.ActualSize.Y, presenter.VerticalOffset, presenter.ViewportHeight)
: (offsetXY.X, element.ActualSize.X, presenter.HorizontalOffset, presenter.ViewportWidth);

Expand All @@ -130,7 +132,7 @@ ItemsPanelRoot.PhysicalOrientation is Orientation.Vertical
? elementOffset - presenterViewportLength + elementLength
: elementOffset;

if (ItemsPanelRoot.PhysicalOrientation is Orientation.Vertical)
if (orientation is Orientation.Vertical)
{
sv.ScrollToVerticalOffset(newOffset);
}
Expand Down
8 changes: 7 additions & 1 deletion src/Uno.UI/UI/Xaml/Controls/Primitives/Selector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,13 @@ internal virtual void OnSelectedItemChanged(object oldSelectedItem, object selec
#if !IS_UNIT_TESTS
if (newIndex != -1 && IsInLiveTree)
{
if (this is ListViewBase lvb)
if (this is ListViewBase lvb
#if __IOS__
// workaround to prevent scrolling when it is not ready
// without this, the ios TabView could render blank if the selection happens too early.
&& ContainerFromIndex(newIndex) is FrameworkElement { IsLoaded: true }
#endif
)
{
#if __IOS__ || __ANDROID__
lvb.InstantScrollToIndex(newIndex);
Expand Down

0 comments on commit 5384cc4

Please sign in to comment.