From d0f903c5f791ab955ff55fdf5f8a2eff07ca2033 Mon Sep 17 00:00:00 2001 From: xiaoy312 Date: Wed, 27 Nov 2024 15:53:00 -0500 Subject: [PATCH 1/2] fix(tabbar): initial orientation and selection not working --- src/Uno.Toolkit.UI/Controls/TabBar/TabBar.cs | 10 +++++++++- .../Controls/TabBar/TabBarListPanel.cs | 15 +++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/Uno.Toolkit.UI/Controls/TabBar/TabBar.cs b/src/Uno.Toolkit.UI/Controls/TabBar/TabBar.cs index 5ed95ec11..5193cf862 100644 --- a/src/Uno.Toolkit.UI/Controls/TabBar/TabBar.cs +++ b/src/Uno.Toolkit.UI/Controls/TabBar/TabBar.cs @@ -195,6 +195,14 @@ private void OnLoaded(object sender, RoutedEventArgs e) UpdateOrientation(); } + internal void OnItemsPanelConnected(TabBarListPanel panel) + { + System.Diagnostics.Debug.Assert(ItemsPanelRoot != null, "ItemsPanelRoot is expected to be already set in here."); + + SynchronizeInitialSelection(); + UpdateOrientation(); + } + private void OnTabBarItemClick(object sender, RoutedEventArgs e) { if (_isSynchronizingSelection) @@ -441,7 +449,7 @@ private void RaiseSelectionChangedEvent(object? prevItem, object? nextItem) return null; } - private bool IsReady => _isLoaded && HasItems; + private bool IsReady => _isLoaded && HasItems && ItemsPanelRoot is { }; private bool HasItems => this.GetItems().Any(); } diff --git a/src/Uno.Toolkit.UI/Controls/TabBar/TabBarListPanel.cs b/src/Uno.Toolkit.UI/Controls/TabBar/TabBarListPanel.cs index b75aaf065..1cf455530 100644 --- a/src/Uno.Toolkit.UI/Controls/TabBar/TabBarListPanel.cs +++ b/src/Uno.Toolkit.UI/Controls/TabBar/TabBarListPanel.cs @@ -17,6 +17,7 @@ namespace Uno.Toolkit.UI { public partial class TabBarListPanel : Panel { + #region Orientation public Orientation Orientation { get { return (Orientation)GetValue(OrientationProperty); } @@ -28,6 +29,20 @@ public Orientation Orientation typeof(Orientation), typeof(TabBarListPanel), new PropertyMetadata(Orientation.Horizontal, (s, e) => ((TabBarListPanel)s).OnPropertyChanged(e))); + #endregion + + public TabBarListPanel() + { + this.Loaded += OnLoaded; + } + + private void OnLoaded(object sender, RoutedEventArgs e) + { + var owner = this.FindFirstParent(); + + // workaround for #1287 ItemsPanelRoot resolution timing related issue + owner?.OnItemsPanelConnected(this); + } private void OnPropertyChanged(DependencyPropertyChangedEventArgs args) { From abc36922be8204a15ba361636769fcf048b73a60 Mon Sep 17 00:00:00 2001 From: xiaoy312 Date: Wed, 27 Nov 2024 16:13:39 -0500 Subject: [PATCH 2/2] fix(chipgroup): initial selection --- .../Controls/Chips/ChipGroup.cs | 23 +++++++++++++++--- .../Controls/TabBar/TabBarListPanel.cs | 24 ++++++++++--------- .../Extensions/ItemsControlExtensions.cs | 8 ------- 3 files changed, 33 insertions(+), 22 deletions(-) diff --git a/src/Uno.Toolkit.UI/Controls/Chips/ChipGroup.cs b/src/Uno.Toolkit.UI/Controls/Chips/ChipGroup.cs index 761b5ef71..84f022f59 100644 --- a/src/Uno.Toolkit.UI/Controls/Chips/ChipGroup.cs +++ b/src/Uno.Toolkit.UI/Controls/Chips/ChipGroup.cs @@ -3,6 +3,8 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using Uno.UI.Extensions; + #if IS_WINUI using Microsoft.UI.Xaml; @@ -18,7 +20,6 @@ namespace Uno.Toolkit.UI { public partial class ChipGroup : ItemsControl { - private bool _isLoaded; private bool _isSynchronizingSelection; public ChipGroup() @@ -31,11 +32,26 @@ public ChipGroup() protected override void OnApplyTemplate() { base.OnApplyTemplate(); + + // workaround for #1287 ItemsPanelRoot resolution timing related issue + var presenter = + this.GetTemplateRoot() as ItemsPresenter ?? + this.GetFirstDescendant(); + if (presenter is { }) + { + presenter.Loaded += OnItemsPresenterLoaded; + } } private void OnLoaded(object sender, RoutedEventArgs e) { - _isLoaded = true; + SynchronizeInitialSelection(); + EnforceSelectionMode(); + ApplyIconTemplate(null, IconTemplate); + } + + private void OnItemsPresenterLoaded(object sender, RoutedEventArgs e) + { SynchronizeInitialSelection(); EnforceSelectionMode(); ApplyIconTemplate(null, IconTemplate); @@ -58,6 +74,7 @@ private void OnSelectionMemberPathChanged(DependencyPropertyChangedEventArgs e) private void ApplyIconTemplate(DataTemplate? oldTemplate, DataTemplate? newTemplate) { + if (!IsReady) return; if (oldTemplate == newTemplate) return; foreach (var container in this.GetItemContainers()) @@ -403,7 +420,7 @@ bool ShouldClearSelection(Chip container) } } - private bool IsReady => _isLoaded && HasItems && HasContainers; + private bool IsReady => IsLoaded && HasItems && HasContainers; private bool HasItems => this.GetItems().OfType().Any(); diff --git a/src/Uno.Toolkit.UI/Controls/TabBar/TabBarListPanel.cs b/src/Uno.Toolkit.UI/Controls/TabBar/TabBarListPanel.cs index 1cf455530..ada679ee7 100644 --- a/src/Uno.Toolkit.UI/Controls/TabBar/TabBarListPanel.cs +++ b/src/Uno.Toolkit.UI/Controls/TabBar/TabBarListPanel.cs @@ -17,18 +17,20 @@ namespace Uno.Toolkit.UI { public partial class TabBarListPanel : Panel { - #region Orientation - public Orientation Orientation - { - get { return (Orientation)GetValue(OrientationProperty); } - set { SetValue(OrientationProperty, value); } - } + #region DependencyProperty: Orientation public static DependencyProperty OrientationProperty { get; } = DependencyProperty.Register( nameof(Orientation), typeof(Orientation), typeof(TabBarListPanel), - new PropertyMetadata(Orientation.Horizontal, (s, e) => ((TabBarListPanel)s).OnPropertyChanged(e))); + new PropertyMetadata(default(Orientation), OnOrientationChanged)); + + public Orientation Orientation + { + get => (Orientation)GetValue(OrientationProperty); + set => SetValue(OrientationProperty, value); + } + #endregion public TabBarListPanel() @@ -44,11 +46,11 @@ private void OnLoaded(object sender, RoutedEventArgs e) owner?.OnItemsPanelConnected(this); } - private void OnPropertyChanged(DependencyPropertyChangedEventArgs args) + private static void OnOrientationChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { - if (args.Property == OrientationProperty) + if (sender is TabBar owner) { - InvalidateMeasure(); + owner.InvalidateMeasure(); } } @@ -147,6 +149,6 @@ protected override Size ArrangeOverride(Size finalSize) return finalSize; } - private bool IsVisible(UIElement x) => x.Visibility == Visibility.Visible; + private static bool IsVisible(UIElement x) => x.Visibility == Visibility.Visible; } } diff --git a/src/Uno.Toolkit.UI/Extensions/ItemsControlExtensions.cs b/src/Uno.Toolkit.UI/Extensions/ItemsControlExtensions.cs index 2d3e67280..5a206833b 100644 --- a/src/Uno.Toolkit.UI/Extensions/ItemsControlExtensions.cs +++ b/src/Uno.Toolkit.UI/Extensions/ItemsControlExtensions.cs @@ -56,14 +56,6 @@ 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(); ///