Skip to content

Commit f613c34

Browse files
Merge pull request #307 from syncfusion/TabViewHeaderItemSpacing
Provide support to customize the spacing between header items in Tab View
2 parents 62d8f2a + 46ca0a2 commit f613c34

File tree

3 files changed

+152
-27
lines changed

3 files changed

+152
-27
lines changed

maui/src/TabView/Control/SfTabBar.cs

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ internal partial class SfTabBar : SfStackLayout, ITapGestureListener
3030

3131
// Dimension and positioning fields
3232

33-
readonly int _defaultTextPadding = 36;
3433
double _previousTabX = 0d;
3534
double _selectedTabX = 0d;
3635
double _currentIndicatorWidth = 0d;
@@ -215,6 +214,17 @@ internal partial class SfTabBar : SfStackLayout, ITapGestureListener
215214
typeof(SfTabBar),
216215
propertyChanged: OnHeaderTemplateChanged);
217216

217+
/// <summary>
218+
/// Identifies the <see cref="HeaderItemSpacing"/> bindable property.
219+
/// </summary>
220+
internal static readonly BindableProperty HeaderItemSpacingProperty =
221+
BindableProperty.Create(
222+
nameof(HeaderItemSpacing),
223+
typeof(int),
224+
typeof(SfTabBar),
225+
36,
226+
propertyChanged: OnHeaderItemSpacingChanged);
227+
218228
/// <summary>
219229
/// Identifies the <see cref="HeaderDisplayMode"/> bindable property.
220230
/// </summary>
@@ -513,6 +523,18 @@ internal DataTemplate? HeaderItemTemplate
513523
set => SetValue(HeaderItemTemplateProperty, value);
514524
}
515525

526+
/// <summary>
527+
/// Gets or sets the value that defines the spacing between header items.
528+
/// </summary>
529+
/// <value>
530+
/// It accepts int values and the default value is 36.
531+
/// </value>
532+
internal int HeaderItemSpacing
533+
{
534+
get => (int)GetValue(HeaderItemSpacingProperty);
535+
set => SetValue(HeaderItemSpacingProperty, value);
536+
}
537+
516538
/// <summary>
517539
/// Gets or sets a value that can be used to customize the corner radius of selection indicator.
518540
/// </summary>
@@ -1481,6 +1503,11 @@ void UpdateTabPadding()
14811503
CalculateTabItemsSourceWidth();
14821504
}
14831505

1506+
void UpdateTabHeaderItemSpacing()
1507+
{
1508+
CalculateTabItemWidth();
1509+
}
1510+
14841511
void AddTabViewItemFromTemplate(object? item)
14851512
{
14861513
var view = item as View;
@@ -2125,8 +2152,8 @@ void CalculateTabItemWidthForCustomMode()
21252152
if (item.Header != null)
21262153
{
21272154
Size desiredSize = item.Header.Measure((float)item.FontSize, item.FontAttributes, item.FontFamily);
2128-
double width = desiredSize.Width + _defaultTextPadding;
2129-
UpdateTabItemWidth(item, item.IsSelected ? width : _tabHeaderImageSize + _defaultTextPadding);
2155+
double width = desiredSize.Width + HeaderItemSpacing;
2156+
UpdateTabItemWidth(item, item.IsSelected ? width : _tabHeaderImageSize + HeaderItemSpacing);
21302157
}
21312158
}
21322159
}
@@ -2163,7 +2190,7 @@ void CalculateTabItemWidthForSizeToContentForItem(SfTabItem item, Size desiredSi
21632190
if (item.HeaderContent != null)
21642191
{
21652192
Size headerContentSize = item.HeaderContent.Measure(double.PositiveInfinity, double.PositiveInfinity);
2166-
width = headerContentSize.Width + _defaultTextPadding;
2193+
width = headerContentSize.Width + HeaderItemSpacing;
21672194
}
21682195
else if (item.ImageSource != null && !string.IsNullOrEmpty(item.Header))
21692196
{
@@ -2172,12 +2199,12 @@ void CalculateTabItemWidthForSizeToContentForItem(SfTabItem item, Size desiredSi
21722199
else if (item.ImageSource != null)
21732200
{
21742201
item.HeaderDisplayMode = TabBarDisplayMode.Image;
2175-
width = _tabHeaderImageSize + _defaultTextPadding;
2202+
width = _tabHeaderImageSize + HeaderItemSpacing;
21762203
}
21772204
else
21782205
{
21792206
item.HeaderDisplayMode = TabBarDisplayMode.Text;
2180-
width = desiredSize.Width + _defaultTextPadding;
2207+
width = desiredSize.Width + HeaderItemSpacing;
21812208
}
21822209
UpdateTabItemWidth(item, width);
21832210
}
@@ -2189,22 +2216,22 @@ double GetWidthForSizeToContentWithImageAndText(SfTabItem item, Size desiredSize
21892216
if (HeaderDisplayMode == TabBarDisplayMode.Image)
21902217
{
21912218
item.HeaderDisplayMode = TabBarDisplayMode.Image;
2192-
return _tabHeaderImageSize + _defaultTextPadding;
2219+
return _tabHeaderImageSize + HeaderItemSpacing;
21932220
}
21942221
else if (HeaderDisplayMode == TabBarDisplayMode.Text)
21952222
{
21962223
item.HeaderDisplayMode = TabBarDisplayMode.Text;
2197-
return desiredSize.Width + _defaultTextPadding;
2224+
return desiredSize.Width + HeaderItemSpacing;
21982225
}
21992226
else
22002227
{
22012228
if (item.ImagePosition == TabImagePosition.Left || item.ImagePosition == TabImagePosition.Right)
22022229
{
2203-
return desiredSize.Width + _defaultTextPadding + _tabHeaderImageSize + item.ImageTextSpacing;
2230+
return desiredSize.Width + HeaderItemSpacing + _tabHeaderImageSize + item.ImageTextSpacing;
22042231
}
22052232
else
22062233
{
2207-
return desiredSize.Width + _defaultTextPadding;
2234+
return desiredSize.Width + HeaderItemSpacing;
22082235
}
22092236
}
22102237
}
@@ -3463,6 +3490,8 @@ protected override void OnBindingContextChanged()
34633490

34643491
static void OnHeaderTemplateChanged(BindableObject bindable, object oldValue, object newValue) => (bindable as SfTabBar)?.UpdateItemsSource();
34653492

3493+
static void OnHeaderItemSpacingChanged(BindableObject bindable, object oldValue, object newValue) => (bindable as SfTabBar)?.UpdateTabHeaderItemSpacing();
3494+
34663495
static void OnItemsSourceChanged(BindableObject bindable, object oldValue, object newValue) => (bindable as SfTabBar)?.UpdateItemsSource();
34673496

34683497
static void OnSelectedIndexChanged(BindableObject bindable, object oldValue, object newValue) => (bindable as SfTabBar)?.UpdateSelectedIndex((int)newValue, (int)oldValue);

maui/src/TabView/Control/SfTabView.cs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,17 @@ public partial class SfTabView : ContentView, IParentThemeElement
228228
null,
229229
propertyChanged: OnHeaderItemTemplateChanged);
230230

231+
/// <summary>
232+
/// Identifies the <see cref="HeaderItemSpacing"/> bindable property.
233+
/// </summary>
234+
public static readonly BindableProperty HeaderItemSpacingProperty =
235+
BindableProperty.Create(
236+
nameof(HeaderItemSpacing),
237+
typeof(int),
238+
typeof(SfTabView),
239+
36,
240+
propertyChanged: OnHeaderItemSpacingChanged);
241+
231242
/// <summary>
232243
/// Identifies the <see cref="ContentItemTemplate"/> bindable property.
233244
/// </summary>
@@ -1033,6 +1044,33 @@ public DataTemplate? HeaderItemTemplate
10331044
get => (DataTemplate?)GetValue(HeaderItemTemplateProperty);
10341045
set => SetValue(HeaderItemTemplateProperty, value);
10351046
}
1047+
1048+
/// <summary>
1049+
/// Gets or sets the value that defines the spacing between header items.
1050+
/// </summary>
1051+
/// <value>
1052+
/// It accepts int values and the default value is 36.
1053+
/// </value>
1054+
/// <example>
1055+
/// Here is an example of how to set the <see cref="HeaderItemSpacing"/> property.
1056+
///
1057+
/// # [XAML](#tab/tabid-1)
1058+
/// <code><![CDATA[
1059+
/// <tabView:SfTabView HeaderItemSpacing="32" />
1060+
/// ]]></code>
1061+
///
1062+
/// # [C#](#tab/tabid-2)
1063+
/// <code><![CDATA[
1064+
/// SfTabView tabView = new SfTabView();
1065+
/// tabView.HeaderItemSpacing = 32;
1066+
/// Content = tabView;
1067+
/// ]]></code>
1068+
/// </example>
1069+
public int HeaderItemSpacing
1070+
{
1071+
get => (int)GetValue(HeaderItemSpacingProperty);
1072+
set => SetValue(HeaderItemSpacingProperty, value);
1073+
}
10361074

10371075
/// <summary>
10381076
/// Gets or sets the template that is used to display the content.
@@ -1948,6 +1986,11 @@ internal void RaiseCenterButtonTappedEvent(EventArgs args)
19481986
/// </summary>
19491987
static void OnHeaderItemTemplateChanged(BindableObject bindable, object oldValue, object newValue) => (bindable as SfTabView)?.UpdateHeaderItemTemplate();
19501988

1989+
/// <summary>
1990+
/// Handles changes to the <see cref="HeaderItemSpacing"/> property.
1991+
/// </summary>
1992+
static void OnHeaderItemSpacingChanged(BindableObject bindable, object oldValue, object newValue) => (bindable as SfTabView)?.UpdateHeaderSpacing();
1993+
19511994
/// <summary>
19521995
/// Handles changes to the <see cref="ContentItemTemplate"/> property.
19531996
/// </summary>
@@ -2083,6 +2126,17 @@ void UpdateHeaderItemTemplate()
20832126
}
20842127
}
20852128

2129+
/// <summary>
2130+
/// Updates the header item spacing of the tab bar.
2131+
/// </summary>
2132+
void UpdateHeaderSpacing()
2133+
{
2134+
if (_tabHeaderContainer != null)
2135+
{
2136+
_tabHeaderContainer.HeaderItemSpacing = HeaderItemSpacing;
2137+
}
2138+
}
2139+
20862140
/// <summary>
20872141
/// Updates the content item template if both ItemsSource and ContentItemTemplate are set.
20882142
/// </summary>

maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Navigation/SfTabViewUnitTests.cs

Lines changed: 59 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1306,23 +1306,6 @@ public void TestIsScrollButtonEnabledPropertyShouldUpdateValue()
13061306
Assert.True(tabBar.IsScrollButtonEnabled);
13071307
}
13081308

1309-
[Fact]
1310-
public void TestGetPrivateFieldShouldReturnExpectedValue()
1311-
{
1312-
// Assuming there's a private field named '_defaultTextPadding'
1313-
var value = GetPrivateField(tabBar, "_defaultTextPadding");
1314-
Assert.Equal(36, value);
1315-
}
1316-
1317-
[Fact]
1318-
public void SetPrivateFieldShouldUpdateValue()
1319-
{
1320-
// Assuming there's a private field named '_defaultTextPadding'
1321-
SetPrivateField(tabBar, "_defaultTextPadding", 50);
1322-
var value = GetPrivateField(tabBar, "_defaultTextPadding");
1323-
Assert.Equal(50, value);
1324-
}
1325-
13261309
[Fact]
13271310
public void TestItemsPropertyShouldUpdateValue()
13281311
{
@@ -8881,6 +8864,65 @@ public void AnimationEasing_RuntimeChange_UpdatesChildComponents()
88818864

88828865
}
88838866
#endregion
8867+
#region HeaderItemSpacing
8868+
8869+
[Fact]
8870+
public void HeaderItemSpacing_DefaultValue_ShouldBe36()
8871+
{
8872+
var tabView = new SfTabView();
8873+
8874+
Assert.Equal(36, tabView.HeaderItemSpacing);
8875+
}
8876+
8877+
[Theory]
8878+
[InlineData(10)]
8879+
[InlineData(0)]
8880+
[InlineData(-5)]
8881+
public void HeaderItemSpacing_SetAndGet_ReturnsExpectedValue(int expected)
8882+
{
8883+
var tabView = new SfTabView
8884+
{
8885+
HeaderItemSpacing = expected
8886+
};
8887+
8888+
Assert.Equal(expected, tabView.HeaderItemSpacing);
8889+
}
8890+
8891+
[Fact]
8892+
public void HeaderItemSpacing_PropertyChanged_TriggersEvent_AndUpdatesTabBar()
8893+
{
8894+
var tabView = new SfTabView();
8895+
var tabBar = GetPrivateField<SfTabView>(tabView, "_tabHeaderContainer") as SfTabBar;
8896+
8897+
bool eventTriggered = false;
8898+
string? changedPropertyName = null;
8899+
8900+
tabView.PropertyChanged += (sender, e) =>
8901+
{
8902+
if (e.PropertyName == nameof(SfTabView.HeaderItemSpacing))
8903+
{
8904+
eventTriggered = true;
8905+
changedPropertyName = e.PropertyName;
8906+
}
8907+
};
8908+
8909+
int newValue = 20;
8910+
tabView.HeaderItemSpacing = newValue;
8911+
8912+
Assert.True(eventTriggered);
8913+
Assert.Equal(nameof(SfTabView.HeaderItemSpacing), changedPropertyName);
8914+
Assert.NotNull(tabBar);
8915+
Assert.Equal(newValue, tabBar.HeaderItemSpacing);
8916+
8917+
// Test with another value
8918+
newValue = -10;
8919+
tabView.HeaderItemSpacing = newValue;
8920+
8921+
Assert.Equal(newValue, tabBar.HeaderItemSpacing);
8922+
}
8923+
8924+
#endregion
8925+
88848926
#region EnableRippleAnimation Unit Tests
88858927

88868928
[Fact]

0 commit comments

Comments
 (0)