Skip to content

Commit 8f923fb

Browse files
update sample
1 parent d9c90f8 commit 8f923fb

File tree

6 files changed

+148
-72
lines changed

6 files changed

+148
-72
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: 101 additions & 33 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,75 +113,93 @@
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"/>
107170
</mauiTabView:Tab.Content>
108171
</mauiTabView:Tab>
109172
</mauiTabView:TabView.Tabs>
110173
</mauiTabView:TabView>
111174

112-
<Line X1="0"
113-
Y1="0"
114-
X2="{Binding Width, Source={x:Reference Page}}"
115-
Y2="0"
116-
Stroke="Red" />
117-
118-
<Label Text="Multiple TabView (using same Tab objects)"
119-
FontSize="20"
120-
HorizontalOptions="Center"/>
121-
122-
<mauiTabView:TabView Tabs="{Binding Tabs4}"/>
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" />
185+
</mauiTabView:Tab.Content>
186+
</mauiTabView:Tab>
187+
</mauiTabView:TabView.Tabs>
188+
</mauiTabView:TabView>
123189

124190
<Line X1="0"
125191
Y1="0"
126192
X2="{Binding Width, Source={x:Reference Page}}"
127193
Y2="0"
128194
Stroke="Red" />
129195

130-
<Label Text="TabView Binding"
196+
<!--4-->
197+
<Label Text="Multiple TabView with Binding (using same Tab objects)"
131198
FontSize="20"
132199
HorizontalOptions="Center"/>
133200

134201
<mauiTabView:TabView Tabs="{Binding Tabs3}"/>
202+
<mauiTabView:TabView Tabs="{Binding Tabs3}"/>
135203

136204
</VerticalStackLayout>
137205
</ScrollView>

MauiTabView/MainPage.xaml.cs

Lines changed: 16 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -18,57 +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();
24-
public ObservableCollection<Tab> Tabs4 { get; set; } = new();
2524

2625
public MainViewModel()
2726
{
28-
Tabs.Add(new Tab()
27+
Tabs1.Add(new Tab()
2928
{
30-
Title = "Tab1",
31-
Content = new Label() { Text = "Tab1 Label" },
29+
Title = "Tab11",
30+
Content = new Label() { Text = "Tab11 Label" },
3231
Icon = "cat.png"
3332
});
34-
Tabs.Add(new Tab()
33+
Tabs1.Add(new Tab()
3534
{
36-
Title = "Tab2",
37-
Content = new Label() { Text = "Tab2 Label" },
35+
Title = "Tab12",
36+
Content = new Label() { Text = "Tab12 Label" },
3837
Icon = "dog.png"
3938
});
4039
Tabs2.Add(new Tab()
4140
{
42-
Title = "Tab1",
43-
Content = new Label() { Text = "Tab1 Label" },
41+
Title = "Tab21",
42+
Content = new Label() { Text = "Tab21 Label" },
4443
Icon = "cat.png"
4544
});
4645
Tabs2.Add(new Tab()
4746
{
48-
Title = "Tab2",
49-
Content = new Label() { Text = "Tab2 Label" },
47+
Title = "Tab22",
48+
Content = new Label() { Text = "Tab22 Label" },
5049
Icon = "dog.png"
5150
});
5251
Tabs3.Add(new Tab()
5352
{
54-
Title = "Tab1",
55-
Content = new Label() { Text = "Tab1 Label" },
53+
Title = "Tab31",
54+
Content = new Label() { Text = "Tab31 Label" },
5655
Icon = "cat.png"
5756
});
5857
Tabs3.Add(new Tab()
5958
{
60-
Title = "Tab2",
61-
Content = new Label() { Text = "Tab2 Label" },
59+
Title = "Tab32",
60+
Content = new Label() { Text = "Tab32 Label" },
6261
Icon = "dog.png"
6362
});
64-
65-
// Tabs4 shares the same Tab objects as Tabs3 to demonstrate the fix
66-
// This would previously cause "The specified child already has a parent" exception
67-
foreach (var tab in Tabs3)
68-
{
69-
Tabs4.Add(tab);
70-
}
71-
63+
7264
SelectedTab = Tabs2[0];
7365
}
7466
}

MauiTabView/TabView.cs

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,43 +3,46 @@ namespace MauiTabView;
33

44
using System.Collections.ObjectModel;
55
using Maui.BindableProperty.Generator.Core;
6+
using ILayout = Microsoft.Maui.ILayout;
67

7-
public partial class Tab : View
8+
public partial class Tab : ContentView
89
{
910
[AutoBindable]
1011
private ImageSource? icon;
1112

1213
[AutoBindable]
1314
private string title = string.Empty;
14-
15-
[AutoBindable(DefaultValue = "new ContentView()")]
16-
private IView content = new ContentView();
1715
}
1816

1917
public partial class TabView : VerticalStackLayout
2018
{
21-
[AutoBindable(DefaultValue = "new System.Collections.ObjectModel.ObservableCollection<Tab>()", OnChanged = "OnTabsChanged")]
22-
private ObservableCollection<Tab> tabs = new();
19+
public static readonly BindableProperty ActiveTabIndexProperty = BindableProperty.Create(nameof(ActiveTabIndex), typeof(int), typeof(TabView), -1, propertyChanged: OnActiveTabIndexChanged);
20+
21+
private static void OnActiveTabIndexChanged(BindableObject bindable, object oldValue, object newValue)
22+
{
23+
var tabView = (TabView)bindable;
24+
tabView.OnActiveTabIndexChanged();
25+
}
2326

24-
[AutoBindable(DefaultValue = "-1", OnChanged = "OnActiveTabIndexChanged")]
25-
private int activeTabIndex;
27+
public static readonly BindableProperty TabsProperty = BindableProperty.Create(nameof(Tabs), typeof(ObservableCollection<Tab>), typeof(TabView));
2628

2729
void OnTabsChanged()
2830
{
2931
Children.Clear();
3032
Children.Add(BuildTabs());
31-
OnActiveTabIndexChanged();
32-
ActiveTabIndex = Tabs.Count > 0 ? 0 : -1;
33+
ActiveTabIndex = -1;
3334
}
3435

3536
public TabView()
3637
{
37-
Loaded += TabView_Loaded;
38+
Tabs = new ObservableCollection<Tab>();
39+
Loaded += OnLoaded;
3840
}
3941

40-
private void TabView_Loaded(object? sender, EventArgs e)
42+
private void OnLoaded(object? sender, EventArgs e)
4143
{
4244
OnTabsChanged();
45+
Loaded -= OnLoaded;
4346
}
4447

4548
void OnActiveTabIndexChanged()
@@ -52,9 +55,9 @@ void OnActiveTabIndexChanged()
5255

5356
// Remove the view from its current parent if it has one
5457
// This is necessary when multiple TabView instances share the same Tab objects
55-
if (activeTab is Element element && element.Parent is ILayout parentLayout)
58+
if (activeTab.Parent is ILayout parentLayout)
5659
{
57-
parentLayout.Remove(element);
60+
parentLayout.Remove(activeTab);
5861
}
5962

6063
if (Children.Count == 1)
@@ -95,6 +98,7 @@ IView BuildTabs()
9598

9699
return view;
97100
}
101+
98102
IView? GetActiveTab()
99103
{
100104
if (Tabs.Count <= ActiveTabIndex || ActiveTabIndex < 0)
@@ -105,4 +109,16 @@ IView BuildTabs()
105109
var activeTab = Tabs[ActiveTabIndex];
106110
return activeTab.Content;
107111
}
112+
113+
public ObservableCollection<Tab> Tabs
114+
{
115+
get => (ObservableCollection<Tab>)GetValue(TabsProperty);
116+
set => SetValue(TabsProperty, value);
117+
}
118+
119+
public int ActiveTabIndex
120+
{
121+
get => (int)GetValue(ActiveTabIndexProperty);
122+
set => SetValue(ActiveTabIndexProperty, value);
123+
}
108124
}

0 commit comments

Comments
 (0)