DEV Community

Cover image for Making a TabBar or SegmentedControl in .NET MAUI
David Ortinau
David Ortinau

Posted on

27 2

Making a TabBar or SegmentedControl in .NET MAUI

This is the experience I just created for a little demo app I'm building with .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.

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>


Enter fullscreen mode Exit fullscreen mode

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



<BindableLayout.ItemTemplate>
    <DataTemplate>
        <RadioButton Content="{Binding .}" />
    </DataTemplate>
</BindableLayout.ItemTemplate>


Enter fullscreen mode Exit fullscreen mode

To make this a grouped list of radios, I added a name to the parent layout.



<HorizontalStackLayout 
    RadioButtonGroup.GroupName="MenuCategories">


Enter fullscreen mode Exit fullscreen mode

Image description

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>


Enter fullscreen mode Exit fullscreen mode

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



<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>

                &lt;VisualState x:Name="Unchecked"&gt;
                    &lt;VisualState.Setters&gt;
                        &lt;Setter
                            TargetName="TextLabel"
                            Property="Label.TextColor"
                            Value="White"/&gt;
                        &lt;Setter
                            TargetName="Indicator"
                            Property="BoxView.Color"
                            Value="Transparent"/&gt;
                    &lt;/VisualState.Setters&gt;
                &lt;/VisualState&gt;
            &lt;/VisualStateGroup&gt;
        &lt;/VisualStateGroupList&gt;
    &lt;/VisualStateManager.VisualStateGroups&gt;
    &lt;Label Text="{TemplateBinding Content}" x:Name="TextLabel" /&gt;
    &lt;BoxView x:Name="Indicator" Grid.Row="1" Color="Transparent"/&gt;
&lt;/Grid&gt;
Enter fullscreen mode Exit fullscreen mode

</ControlTemplate>

Enter fullscreen mode Exit fullscreen mode




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.

Hostinger image

Get n8n VPS hosting 3x cheaper than a cloud solution

Get fast, easy, secure n8n VPS hosting from $4.99/mo at Hostinger. Automate any workflow using a pre-installed n8n application and no-code customization.

Start now

Top comments (2)

Collapse
 
gpproton profile image
radioActive DROID • Edited

I've been trying to implement something like this in C# with no luck, any suggestion will be appreciated.

public class SegmentView : Border {
    readonly string[] items = { "Pending", "Confirmed", "Completed" };

    class SegmentButton : Button {
        public SegmentButton() => this.Margins(0, 0, 8, 0);
    }

    public SegmentView() {
        Padding = 3;
        Margin = 8;
        StrokeShape = new RoundRectangle {
            CornerRadius = new CornerRadius(10)
        };
        SetDynamicResource(BackgroundColorProperty, "Tertiary");
        var layout = new HorizontalStackLayout()
            .ItemsSource(items)
            .ItemTemplate(new DataTemplate(() => new RadioButton {
                ControlTemplate = new ControlTemplate(() => new SegmentButton()
                    .Bind(Button.TextProperty, "Content", source: RelativeBindingSource.TemplatedParent))
            }.Bind(RadioButton.ContentProperty)));
        RadioButtonGroup.SetGroupName(layout, "MenuGroup");
        var vsGroups = new VisualStateGroup() {
            Name = "CheckedStates",
        };
        vsGroups.States.Add(new VisualState {
            Name = "Checked",
            Setters = {
                new Setter { TargetName = nameof(SegmentButton), Property = Button.BackgroundColorProperty, Value = Colors.Indigo }
            }
        });
        vsGroups.States.Add(new VisualState {
            Name = "Unchecked",
            Setters = {
                new Setter { TargetName = nameof(SegmentButton), Property = Button.BackgroundColorProperty, Value = Colors.Transparent },
                new Setter { TargetName = nameof(SegmentButton), Property = Button.TextColorProperty, Value = Colors.Indigo }
            }
        });
        VisualStateManager.SetVisualStateGroups(layout,
                new VisualStateGroupList {

                }
            );
        Content = layout;
    }
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
sikandaramla profile image
Sikandar Amla

Simple, clean, elegant

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay