Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 111 additions & 2 deletions maui/src/TabView/Control/HorizontalContent/SfHorizontalContent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ internal partial class SfHorizontalContent : SfTabViewExt, ITouchListener, ITapG
bool? _isTowardsRight;
int _visibleItemCount;
int _currentIndex;
int _previousVisibleIndex;
int _nextVisibleIndex;
bool _isPreviousItemVisible;
bool _isNextItemVisible;
SelectionChangingEventArgs? _selectionChangingEventArgs;
Expand Down Expand Up @@ -103,6 +105,19 @@ internal partial class SfHorizontalContent : SfTabViewExt, ITouchListener, ITapG
typeof(double),
typeof(SfHorizontalContent),
100d);

/// <summary>
/// Identifies the <see cref="EnableVirtualization"/> bindable property.
/// </summary>
/// <value>
/// The identifier for <see cref="EnableVirtualization"/> bindable property.
/// </value>
internal static readonly BindableProperty EnableVirtualizationProperty =
BindableProperty.Create(
nameof(EnableVirtualization),
typeof(bool),
typeof(SfHorizontalContent),
false);
#endregion

#region Properties
Expand Down Expand Up @@ -155,6 +170,18 @@ internal IList? ItemsSource
set => SetValue(ItemsSourceProperty, value);
}

/// <summary>
/// Gets or sets a value indicating whether lazy loading is enabled during the initial load.
/// </summary>
/// <value>
/// It accepts the bool values and the default value is false.
/// </value>
internal bool EnableVirtualization
{
get => (bool)GetValue(EnableVirtualizationProperty);
set => SetValue(EnableVirtualizationProperty, value);
}

/// <summary>
/// Gets or sets the value that defines the content width of each tab header item.
/// </summary>
Expand Down Expand Up @@ -373,6 +400,11 @@ void ClearTabContent(SfTabItem tabItem, int index)
void UpdateSelectedIndex()
{
_isSelectionProcessed = true;
if (EnableVirtualization)
{
LoadItemsContent(SelectedIndex);
}

UpdateTabItemContentPosition();
}

Expand Down Expand Up @@ -439,10 +471,29 @@ void AddTabContentItems(SfTabItem item, int index = -1)
{
if (item.Content != null)
{
SfGrid parentGrid = CreateParentGrid(item);
SfGrid parentGrid = new();

if (!EnableVirtualization || (EnableVirtualization && !(index != SelectedIndex && item.IsVisible)))
{
parentGrid = CreateParentGrid(item);
}

if (index >= 0)
{
_horizontalStackLayout?.Children.Insert(index, parentGrid);
if (EnableVirtualization && index != SelectedIndex && item.IsVisible)
{
var content = new BoxView
{
WidthRequest = 0,
HeightRequest = 0,
Opacity = 0
};
_horizontalStackLayout?.Children.Insert(index, content);
}
else
{
_horizontalStackLayout?.Children.Insert(index, parentGrid);
}
}
else
{
Expand Down Expand Up @@ -849,6 +900,12 @@ void AdjustForFirstIndex(double difference)
{
if (_horizontalStackLayout != null && IsTowardsRight == true)
{
if (EnableVirtualization)
{
var index = _nextVisibleIndex;
LoadItemsContent(index);
}

#if !WINDOWS
if (((this as IVisualElementController).EffectiveFlowDirection & EffectiveFlowDirection.RightToLeft) != EffectiveFlowDirection.RightToLeft)
_horizontalStackLayout.TranslationX = Math.Clamp(_horizontalStackLayout.TranslationX + difference, -ContentWidth, 0);
Expand All @@ -866,6 +923,17 @@ void AdjustForMiddleIndices(double difference)
{
if (_isPreviousItemVisible && _isNextItemVisible)
{
if (EnableVirtualization)
{
int index = -1;
if (IsTowardsRight is not null)
{
index = IsTowardsRight == true ? _nextVisibleIndex : _previousVisibleIndex;
}

LoadItemsContent(index);
}

#if !WINDOWS
if (((this as IVisualElementController).EffectiveFlowDirection & EffectiveFlowDirection.RightToLeft) != EffectiveFlowDirection.RightToLeft)
_horizontalStackLayout.TranslationX = Math.Clamp(_horizontalStackLayout.TranslationX + difference, -ContentWidth * (_currentIndex + 1), -ContentWidth * (_currentIndex - 1));
Expand All @@ -877,6 +945,12 @@ void AdjustForMiddleIndices(double difference)
}
else if (_isPreviousItemVisible)
{
if (EnableVirtualization)
{
var index = _previousVisibleIndex;
LoadItemsContent(index);
}

#if !WINDOWS
if (((this as IVisualElementController).EffectiveFlowDirection & EffectiveFlowDirection.RightToLeft) != EffectiveFlowDirection.RightToLeft)
_horizontalStackLayout.TranslationX = Math.Clamp(_horizontalStackLayout.TranslationX + difference, -ContentWidth * _currentIndex, -ContentWidth * (_currentIndex - 1));
Expand All @@ -888,6 +962,12 @@ void AdjustForMiddleIndices(double difference)
}
else if (_isNextItemVisible)
{
if (EnableVirtualization)
{
var index = _nextVisibleIndex;
LoadItemsContent(index);
}

#if !WINDOWS
if (((this as IVisualElementController).EffectiveFlowDirection & EffectiveFlowDirection.RightToLeft) != EffectiveFlowDirection.RightToLeft)
_horizontalStackLayout.TranslationX = Math.Clamp(_horizontalStackLayout.TranslationX + difference, -ContentWidth * (_currentIndex + 1), -ContentWidth * _currentIndex);
Expand All @@ -904,6 +984,15 @@ void AdjustForLastIndex(double difference)
{
if (_horizontalStackLayout != null)
{
if (EnableVirtualization)
{
var index = _previousVisibleIndex;
if (IsTowardsRight is false)
{
LoadItemsContent(index);
}
}

#if !WINDOWS
if (((this as IVisualElementController).EffectiveFlowDirection & EffectiveFlowDirection.RightToLeft) != EffectiveFlowDirection.RightToLeft)
_horizontalStackLayout.TranslationX = Math.Clamp(_horizontalStackLayout.TranslationX + difference, -ContentWidth * (_visibleItemCount - 1), -ContentWidth * (_visibleItemCount - 2));
Expand All @@ -915,6 +1004,24 @@ void AdjustForLastIndex(double difference)
}
}

/// <summary>
/// This method is used to replace the placeholder view with a tab item when the EnableVirtualization is true.
/// </summary>
/// <param name="index">This index position of the child in the HorizontalStackLayout to replaced with a tab item.</param>
void LoadItemsContent(int index)
{
if (_horizontalStackLayout != null)
{
if (Items is not null && index >= 0 && index < _horizontalStackLayout.Children.Count && _horizontalStackLayout.Children[index] is BoxView)
{
SfGrid parentGrid = CreateParentGrid(Items[index]);
_horizontalStackLayout?.Children.RemoveAt(index);
_horizontalStackLayout?.Children.Insert(index, parentGrid);
UpdateDynamicChanges();
}
}
}

/// <summary>
/// This method is used to update the tab item position after swipe complete.
/// </summary>
Expand Down Expand Up @@ -1377,6 +1484,7 @@ void UpdateVisibilityWithoutTemplate()
if (Items[i].IsVisible == true)
{
_isNextItemVisible = true;
_nextVisibleIndex = i;
break;
}
}
Expand All @@ -1386,6 +1494,7 @@ void UpdateVisibilityWithoutTemplate()
if (Items[i].IsVisible == true)
{
_isPreviousItemVisible = true;
_previousVisibleIndex = i;
break;
}
}
Expand Down
6 changes: 5 additions & 1 deletion maui/src/TabView/Control/SfTabBar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -720,7 +720,11 @@ internal void UpdateSelectedIndex(int newIndex, int oldIndex)
{
if (newIndex != -1)
{
_isSelectionProcessed = true;
if (IsLoaded)
{
_isSelectionProcessed = true;
}

UpdateSelectedTabItemIsSelected(newIndex, oldIndex);
UpdateTabIndicatorWidth();
if (_tabSelectionChangedEventArgs != null)
Expand Down
51 changes: 51 additions & 0 deletions maui/src/TabView/Control/SfTabView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,17 @@ public class SfTabView : ContentView, IParentThemeElement
BindingMode.Default,
null);

/// <summary>
/// Identifies the <see cref="EnableVirtualization"/> bindable property.
/// </summary>
public static readonly BindableProperty EnableVirtualizationProperty =
BindableProperty.Create(
nameof(EnableVirtualization),
typeof(bool),
typeof(SfTabView),
false,
propertyChanged: OnEnableVirtualizationChanged);

static readonly BindableProperty IsContentTransitionEnabledProperty =
BindableProperty.Create(
nameof(IsContentTransitionEnabled),
Expand Down Expand Up @@ -1257,6 +1268,32 @@ public double ContentTransitionDuration
set => SetValue(ContentTransitionDurationProperty, value);
}

/// <summary>
/// Gets or sets a value indicating whether lazy loading is enabled during the initial load.
/// </summary>
/// <value>
/// A boolean value indicating whether lazy loading is enabled. The default value is false.
/// </value>
/// <example>
/// Here is an example of how to set the <see cref="EnableVirtualization"/> property.
///
/// # [XAML](#tab/tabid-1)
/// <code><![CDATA[
/// <tabView:SfTabView EnableVirtualization="True" />
/// ]]></code>
///
/// # [C#](#tab/tabid-2)
/// <code><![CDATA[
/// SfTabView tabView = new SfTabView();
/// tabView.EnableVirtualization = true;
/// ]]></code>
/// </example>
public bool EnableVirtualization
{
get => (bool)GetValue(EnableVirtualizationProperty);
set => SetValue(EnableVirtualizationProperty, value);
}

/// <summary>
/// Gets or sets a value that can be used to customize the scroll button’s background color in the <see cref="SfTabView"/>.
/// </summary>
Expand Down Expand Up @@ -1557,6 +1594,11 @@ internal void RaiseSelectionChangingEvent(SelectionChangingEventArgs args)
/// </summary>
static void OnFontAutoScalingEnabledChanged(BindableObject bindable, object oldValue, object newValue) => (bindable as SfTabView)?.UpdateFontAutoScalingEnabled((Boolean)newValue);

/// <summary>
/// Handles changes to the <see cref="EnableVirtualization"/> property.
/// </summary>
static void OnEnableVirtualizationChanged(BindableObject bindable, object oldValue, object newValue) => (bindable as SfTabView)?.UpdateEnableVirtualization();

#endregion

#region Private Methods
Expand Down Expand Up @@ -1955,6 +1997,15 @@ void UpdateFontAutoScalingEnabled(bool newValue)
}
}

/// <summary>
/// Updates the enable virtualization.
/// </summary>
void UpdateEnableVirtualization()
{
if (_tabContentContainer != null)
_tabContentContainer.EnableVirtualization = EnableVirtualization;
}

/// <summary>
/// Gets the theme dictionary for the tab view.
/// </summary>
Expand Down