Skip to content

Commit 3a5cd4b

Browse files
Fix: Support multiple TabView instances sharing Tab objects (#454)
Co-authored-by: VladislavAntonyuk <[email protected]> Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: Vladislav Antonyuk <[email protected]>
1 parent 2346f71 commit 3a5cd4b

File tree

7 files changed

+173
-51
lines changed

7 files changed

+173
-51
lines changed

Directory.Packages.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
<PackageVersion Include="BenchmarkDotNet" Version="0.15.4" />
1111
<PackageVersion Include="Camera.MAUI.ZXing" Version="1.0.0" />
1212
<PackageVersion Include="ClosedXML" Version="0.105.0" />
13-
<PackageVersion Include="CommunityToolkit.Maui" Version="12.1.0" />
13+
<PackageVersion Include="CommunityToolkit.Maui" Version="99.0.0-pre1" />
1414
<PackageVersion Include="CommunityToolkit.Maui.Camera" Version="99.0.0-pre1" />
1515
<PackageVersion Include="CommunityToolkit.Maui.Maps" Version="3.0.2" />
1616
<PackageVersion Include="CommunityToolkit.Maui.MediaElement" Version="6.1.1" />
918 KB
Binary file not shown.
300 KB
Binary file not shown.

MauiTabView/MainPage.xaml

Lines changed: 102 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,68 @@
33
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
44
xmlns:mauiTabView="clr-namespace:MauiTabView"
55
x:Class="MauiTabView.MainPage"
6-
x:Name="Page">
6+
x:Name="Page"
7+
x:DataType="mauiTabView:MainViewModel">
78

89
<ContentPage.Resources>
9-
<ControlTemplate x:Key="TabControlTemplate">
10-
<VerticalStackLayout BindingContext="{Binding Source={RelativeSource TemplatedParent}}">
11-
<Image Source="{Binding Value.Icon}"
12-
WidthRequest="30"
13-
HeightRequest="30"
14-
HorizontalOptions="Center"/>
15-
<Label Text="{Binding Value.Title}" FontSize="12"
16-
HorizontalOptions="Center"/>
17-
</VerticalStackLayout>
10+
<ControlTemplate x:Key="RadioButtonTemplate">
11+
<Border Stroke="#F3F2F1"
12+
StrokeThickness="2"
13+
StrokeShape="RoundRectangle 10"
14+
BackgroundColor="#F3F2F1"
15+
HeightRequest="90"
16+
WidthRequest="90"
17+
HorizontalOptions="Start"
18+
VerticalOptions="Start">
19+
<VisualStateManager.VisualStateGroups>
20+
<VisualStateGroupList>
21+
<VisualStateGroup x:Name="CheckedStates">
22+
<VisualState x:Name="Checked">
23+
<VisualState.Setters>
24+
<Setter Property="Stroke"
25+
Value="#FF3300" />
26+
<Setter TargetName="check"
27+
Property="Opacity"
28+
Value="1" />
29+
</VisualState.Setters>
30+
</VisualState>
31+
<VisualState x:Name="Unchecked">
32+
<VisualState.Setters>
33+
<Setter Property="BackgroundColor"
34+
Value="#F3F2F1" />
35+
<Setter Property="Stroke"
36+
Value="#F3F2F1" />
37+
<Setter TargetName="check"
38+
Property="Opacity"
39+
Value="0" />
40+
</VisualState.Setters>
41+
</VisualState>
42+
</VisualStateGroup>
43+
</VisualStateGroupList>
44+
</VisualStateManager.VisualStateGroups>
45+
<Grid Margin="4"
46+
WidthRequest="90">
47+
<Grid Margin="0,0,4,0"
48+
WidthRequest="18"
49+
HeightRequest="18"
50+
HorizontalOptions="End"
51+
VerticalOptions="Start">
52+
<Ellipse Stroke="Blue"
53+
Fill="White"
54+
WidthRequest="16"
55+
HeightRequest="16"
56+
HorizontalOptions="Center"
57+
VerticalOptions="Center" />
58+
<Ellipse x:Name="check"
59+
Fill="Blue"
60+
WidthRequest="8"
61+
HeightRequest="8"
62+
HorizontalOptions="Center"
63+
VerticalOptions="Center" />
64+
</Grid>
65+
<ContentPresenter />
66+
</Grid>
67+
</Border>
1868
</ControlTemplate>
1969

2070
<DataTemplate x:Key="IndicatorDataTemplate">
@@ -35,7 +85,7 @@
3585
Spacing="25"
3686
Padding="30,0"
3787
VerticalOptions="Center">
38-
88+
<!--1-->
3989
<Label Text="Carousel + Indicator"
4090
FontSize="20"
4191
HorizontalOptions="Center"/>
@@ -45,14 +95,14 @@
4595
SelectedIndicatorColor="LightBlue"
4696
IndicatorTemplate="{StaticResource IndicatorDataTemplate}"/>
4797
</Grid>
48-
<CarouselView ItemsSource="{Binding Tabs}"
98+
<CarouselView ItemsSource="{Binding Tabs1}"
4999
IndicatorView="{x:Reference Indicator}"
50100
HorizontalScrollBarVisibility="Never"
51101
Loop="False"
52102
Position="0">
53103
<CarouselView.ItemTemplate>
54104
<DataTemplate x:DataType="mauiTabView:Tab">
55-
<ContentView Content="{Binding Content}"/>
105+
<!-- <ContentView Content="{Binding Content}"/> -->
56106
</DataTemplate>
57107
</CarouselView.ItemTemplate>
58108
</CarouselView>
@@ -63,47 +113,75 @@
63113
Y2="0"
64114
Stroke="Red" />
65115

116+
<!--2-->
66117
<Label Text="RadioButton"
67118
FontSize="20"
68119
HorizontalOptions="Center"/>
69120

70121
<ScrollView Orientation="Horizontal"
71122
HorizontalOptions="Center">
72-
<HorizontalStackLayout RadioButtonGroup.GroupName="tabs"
123+
<HorizontalStackLayout BackgroundColor="Red"
124+
RadioButtonGroup.GroupName="tabs"
73125
BindableLayout.ItemsSource="{Binding Tabs2}"
74126
RadioButtonGroup.SelectedValue="{Binding SelectedTab}">
75127
<BindableLayout.ItemTemplate>
76-
<DataTemplate x:DataType="Tab">
128+
<DataTemplate x:DataType="mauiTabView:Tab">
77129
<RadioButton Value="{Binding }"
78-
ControlTemplate="{StaticResource TabControlTemplate}">
130+
ControlTemplate="{StaticResource RadioButtonTemplate}">
131+
<RadioButton.Content>
132+
<VerticalStackLayout>
133+
<Image Source="{Binding Icon}"
134+
WidthRequest="30"
135+
HeightRequest="30"
136+
HorizontalOptions="Center"/>
137+
<Label Text="{Binding Title}" FontSize="12"
138+
HorizontalOptions="Center"/>
139+
</VerticalStackLayout>
140+
</RadioButton.Content>
79141
</RadioButton>
80142
</DataTemplate>
81143
</BindableLayout.ItemTemplate>
82144
</HorizontalStackLayout>
83145
</ScrollView>
84146

85-
<ContentView Content="{Binding SelectedTab.Content}"/>
147+
<ContentView Content="{Binding SelectedTab}"/>
86148

87149
<Line X1="0"
88150
Y1="0"
89151
X2="{Binding Width, Source={x:Reference Page}}"
90152
Y2="0"
91153
Stroke="Red" />
92154

155+
<!--3-->
93156
<Label Text="TabView"
94157
FontSize="20"
95158
HorizontalOptions="Center"/>
96159

97160
<mauiTabView:TabView>
98161
<mauiTabView:TabView.Tabs>
99-
<mauiTabView:Tab Title="Tab1" Icon="cat.png">
162+
<mauiTabView:Tab Title="Tab1111" Icon="cat.png">
100163
<mauiTabView:Tab.Content>
101-
<Label Text="Tab1 Label"/>
164+
<Label Text="Tab1111 Label"/>
102165
</mauiTabView:Tab.Content>
103166
</mauiTabView:Tab>
104-
<mauiTabView:Tab Title="Tab2" Icon="dog.png">
167+
<mauiTabView:Tab Title="Tab2222" Icon="dog.png">
105168
<mauiTabView:Tab.Content>
106-
<Label Text="Tab2 Label"/>
169+
<Label Text="Tab2222 Label"/>
170+
</mauiTabView:Tab.Content>
171+
</mauiTabView:Tab>
172+
</mauiTabView:TabView.Tabs>
173+
</mauiTabView:TabView>
174+
175+
<mauiTabView:TabView ActiveTabIndex="1">
176+
<mauiTabView:TabView.Tabs>
177+
<mauiTabView:Tab Title="Tab2111" Icon="cat.png">
178+
<mauiTabView:Tab.Content>
179+
<Label Text="Tab2111 Label" />
180+
</mauiTabView:Tab.Content>
181+
</mauiTabView:Tab>
182+
<mauiTabView:Tab Title="Tab2112" Icon="dog.png">
183+
<mauiTabView:Tab.Content>
184+
<Label Text="Tab2112 Label" />
107185
</mauiTabView:Tab.Content>
108186
</mauiTabView:Tab>
109187
</mauiTabView:TabView.Tabs>
@@ -115,11 +193,13 @@
115193
Y2="0"
116194
Stroke="Red" />
117195

118-
<Label Text="TabView Binding"
196+
<!--4-->
197+
<Label Text="Multiple TabView with Binding (using same Tab objects)"
119198
FontSize="20"
120199
HorizontalOptions="Center"/>
121200

122201
<mauiTabView:TabView Tabs="{Binding Tabs3}"/>
202+
<mauiTabView:TabView Tabs="{Binding Tabs3}"/>
123203

124204
</VerticalStackLayout>
125205
</ScrollView>

MauiTabView/MainPage.xaml.cs

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,48 +18,49 @@ public partial class MainViewModel : ObservableObject
1818
[ObservableProperty]
1919
public partial Tab SelectedTab { get; set; }
2020

21-
public ObservableCollection<Tab> Tabs { get; set; } = new();
21+
public ObservableCollection<Tab> Tabs1 { get; set; } = new();
2222
public ObservableCollection<Tab> Tabs2 { get; set; } = new();
2323
public ObservableCollection<Tab> Tabs3 { get; set; } = new();
2424

2525
public MainViewModel()
2626
{
27-
Tabs.Add(new Tab()
27+
Tabs1.Add(new Tab()
2828
{
29-
Title = "Tab1",
30-
Content = new Label() { Text = "Tab1 Label" },
29+
Title = "Tab11",
30+
Content = new Label() { Text = "Tab11 Label" },
3131
Icon = "cat.png"
3232
});
33-
Tabs.Add(new Tab()
33+
Tabs1.Add(new Tab()
3434
{
35-
Title = "Tab2",
36-
Content = new Label() { Text = "Tab2 Label" },
35+
Title = "Tab12",
36+
Content = new Label() { Text = "Tab12 Label" },
3737
Icon = "dog.png"
3838
});
3939
Tabs2.Add(new Tab()
4040
{
41-
Title = "Tab1",
42-
Content = new Label() { Text = "Tab1 Label" },
41+
Title = "Tab21",
42+
Content = new Label() { Text = "Tab21 Label" },
4343
Icon = "cat.png"
4444
});
4545
Tabs2.Add(new Tab()
4646
{
47-
Title = "Tab2",
48-
Content = new Label() { Text = "Tab2 Label" },
47+
Title = "Tab22",
48+
Content = new Label() { Text = "Tab22 Label" },
4949
Icon = "dog.png"
5050
});
5151
Tabs3.Add(new Tab()
5252
{
53-
Title = "Tab1",
54-
Content = new Label() { Text = "Tab1 Label" },
53+
Title = "Tab31",
54+
Content = new Label() { Text = "Tab31 Label" },
5555
Icon = "cat.png"
5656
});
5757
Tabs3.Add(new Tab()
5858
{
59-
Title = "Tab2",
60-
Content = new Label() { Text = "Tab2 Label" },
59+
Title = "Tab32",
60+
Content = new Label() { Text = "Tab32 Label" },
6161
Icon = "dog.png"
6262
});
63+
6364
SelectedTab = Tabs2[0];
6465
}
6566
}

MauiTabView/README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,24 @@
44

55
Article: https://vladislavantonyuk.github.io/articles/Mastering-Composite-Controls-in-.NET-MAUI:-Building-a-TabView-from-Scratch
66

7+
## Features
8+
9+
- Custom TabView control for .NET MAUI
10+
- Support for multiple TabView instances on the same page
11+
- Handles shared Tab objects across multiple TabView instances
12+
- Compatible with Android, iOS, macOS, and Windows
13+
14+
## Multiple TabView Support
15+
16+
You can now use multiple TabView instances on the same page, even when they share the same Tab objects:
17+
18+
```xml
19+
<mauiTabView:TabView Tabs="{Binding Tabs1}"/>
20+
<mauiTabView:TabView Tabs="{Binding Tabs2}"/>
21+
```
22+
23+
The TabView automatically handles the case where Tab objects are shared between multiple TabView instances by removing content from its previous parent before adding it to the new one.
24+
725
## Images
826

927
![.NET MAUI TabView](https://ik.imagekit.io/VladislavAntonyuk/vladislavantonyuk/articles/48/48.png)

0 commit comments

Comments
 (0)