Making a TabBar or SegmentedControl in .NET MAUI
Tags: dotnet, dotnetmaui, programming
Published: 2022-07-09 21:04:19

{% embed https://youtu.be/P3S8xvsc13Y %}

This is the experience I just created for a little demo app I'm building with [.NET MAUI](https://dot.net/maui). Component vendors like Syncfusion and Progress ship SegmentedControls that do this and probably more, but I wanted to see what it would be like to use "in the box" controls. I'm really happy with the result and how easy it was to code. This probably took me 10 minutes.

What I used:

The first two "controls" are really the heart of the solution. The rest are just needed for layout/design. 

* [`RadioButton`](https://docs.microsoft.com/dotnet/maui/user-interface/controls/radiobutton)
* [`BindableLayout`](https://docs.microsoft.com/dotnet/maui/user-interface/layouts/bindablelayout)
* [`HorizontalStackLayout`](https://docs.microsoft.com/dotnet/maui/user-interface/layouts/horizontalstacklayout)
* [`Grid`](https://docs.microsoft.com/dotnet/maui/user-interface/layouts/grid) - perhaps I could have used a `VerticalStackLayout`, but `Grid` gives me great flexibility
* [`Label`](https://docs.microsoft.com/dotnet/maui/user-interface/controls/label)
* [`BoxView`](https://docs.microsoft.com/dotnet/maui/user-interface/controls/boxview) - I like this simple control for painting lines or boxes that need to size to the parent container

## The Code

1 - I created my container with a set of data to be repeated for each tab.

```
<HorizontalStackLayout>
    <BindableLayout.ItemTemplate>
    <!-- Views Here -->
    </BindableLayout.ItemTemplate>
    <BindableLayout.ItemsSource>
        <x:Array Type="{x:Type x:String}">
            <x:String>Hot Dishes</x:String>
            <x:String>Cold Dishes</x:String>
            <x:String>Soups</x:String>
            <x:String>Appetizers</x:String>
            <x:String>Desserts</x:String>
        </x:Array>
    </BindableLayout.ItemsSource>
</HorizontalStackLayout>
```

2 - I created the item template to display a `RadioButton`.

```
<BindableLayout.ItemTemplate>
    <DataTemplate>
        <RadioButton Content="{Binding .}" />
    </DataTemplate>
</BindableLayout.ItemTemplate>
```
 
To make this a grouped list of radios, I added a name to the parent layout.

```
<HorizontalStackLayout 
    RadioButtonGroup.GroupName="MenuCategories">
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r924fw5vypbwvzuhufth.png)

3 - I styled the `RadioButton` using a `ControlTemplate`.

```
<RadioButton Content="{Binding .}">
    <RadioButton.ControlTemplate>
        <ControlTemplate>
            <Grid RowDefinitions="30,4">
                <Label Text="{TemplateBinding Content}" />
                <BoxView Grid.Row="1" Color="Transparent"/>
            </Grid>
        </ControlTemplate>
    </RadioButton.ControlTemplate>
</RadioButton>
```

Because we are inside of a control template, rather than using `Binding` I use `TemplateBinding`. The `Content` could be anything, but I supplied a `String` so it seems safe to bind that directly to the label text.

4 - To get the different look for selected vs unselected, I:

- added a `VisualStateManager` (VSM) to the control template layout
- gave my lable and box names so I could target them from the VSM
- styled the checked and unchecked states (odd naming ¯\_(ツ)_/¯)


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/35b2jv78ku5etpdzjwjx.png)

```
<ControlTemplate>
    <Grid RowDefinitions="30,4">
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroupList>
                <VisualStateGroup x:Name="CheckedStates">
                    <VisualState x:Name="Checked">
                        <VisualState.Setters>
                            <Setter
                                TargetName="TextLabel"
                                Property="Label.TextColor"
                                Value="{StaticResource Primary}"/>
                            <Setter
                                TargetName="Indicator"
                                Property="BoxView.Color"
                                Value="{StaticResource Primary}"/>
                        </VisualState.Setters>
                    </VisualState>

                    <VisualState x:Name="Unchecked">
                        <VisualState.Setters>
                            <Setter
                                TargetName="TextLabel"
                                Property="Label.TextColor"
                                Value="White"/>
                            <Setter
                                TargetName="Indicator"
                                Property="BoxView.Color"
                                Value="Transparent"/>
                        </VisualState.Setters>
                    </VisualState>
                </VisualStateGroup>
            </VisualStateGroupList>
        </VisualStateManager.VisualStateGroups>
        <Label Text="{TemplateBinding Content}" x:Name="TextLabel" />
        <BoxView x:Name="Indicator" Grid.Row="1" Color="Transparent"/>
    </Grid>
</ControlTemplate>
```

## Conclusion

That's it. There are a lot of useful concepts in this example that made implementing it super easy. I hope learned at least one new thing to use in .NET MAUI.