Sharpnado.Tabs icon indicating copy to clipboard operation
Sharpnado.Tabs copied to clipboard

Is there an example on how to dynamically create tabs?

Open balintn22 opened this issue 2 years ago • 4 comments

This component seems just what I need to implement scrollable tabbed pages. Based on your example I understand how to create a scrollable set of tabs, how to link them to tab contents, and I can manipulate them from xaml. However, I need to craeate tabs and tab content dynamically. Is there an eample on how to do it either fully from C# code, instantiating say UnderlinedTabItems and adding them to the TabHostView and the ViewScroller, or via some templating mechanism and using observable collections.

balintn22 avatar Jan 07 '23 21:01 balintn22

Use items source property and item template as you would do with a collection view or a bindable layout

roubachof avatar Jan 07 '23 21:01 roubachof

TabHostView is done, but ViewSwitcher doesn't seem to have ItemSource and ItemTemplate properties I could set. Am I missing something?

balintn22 avatar Jan 08 '23 00:01 balintn22

OK, progress: I checked the source and found that ViewSwitcher is derived from grid, so tried to assign items to grid cells - but that didn't work. Then I tried adding items ti MyViewSwitcher.Children, and it seems to be OK, is paged as the bound TabHostView selected item changes. Here's the code so far:

Page XML relevant parts:

   <ContentPage.Resources>
        <ResourceDictionary>
                <!-- Styles reused from https://github.com/roubachof/Sharpnado.Tabs/blob/main/MauiSample/Presentation/Views/TabM.xaml -->
            <Style x:Key="ScrollableTabStyle" TargetType="tabs:UnderlinedTabItem"> ... </Style>
            <Style x:Key="LabelDescription" TargetType="Label"> ... </Style>

            <DataTemplate x:Key="HeaderTemplate">
                <!-- Bind Label text to List<string> items -->
                <tabs:UnderlinedTabItem
                    Style="{StaticResource ScrollableTabStyle}"
                    Label="{Binding}">
                </tabs:UnderlinedTabItem>
            </DataTemplate>
        </ResourceDictionary>
    </ContentPage.Resources>

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

        <tabs:TabHostView
            x:Name="MyTabHostView"
            Grid.Row="0"
            HeightRequest="50"
            SelectedIndex="1"
            ShowScrollbar="False"
            TabType="Scrollable"
            ItemsSource="{Binding HeaderTexts}"
            ItemTemplate="{StaticResource HeaderTemplate}">
        </tabs:TabHostView>

        <tabs:ViewSwitcher
            x:Name="MyViewSwitcher"
            Grid.Row="1"
            Margin="16,0"
            VerticalOptions="StartAndExpand"
            Animate="False"
            SelectedIndex="{Binding Source={x:Reference MyTabHostView}, Path=SelectedIndex, Mode=OneWay}">
            
            <!-- Tab pages are added as Child Items in code behind -->
        </tabs:ViewSwitcher>
    </Grid>

In code behind:

    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class MyPage : ContentPage
    {
        public MyPage(MyViewModel vm)
        {
            InitializeComponent();
            BindingContext = vm;

            int i = 0;
            foreach(var contentGroup in vm.ContentGroups)
            {
                Label label = new Label
                {
                    Text = $"Tab {+i} content",
                    Style = (Style)this.Resources["LabelDescription"]
                };
                MyViewSwitcher.Children.Add(label);
            }

            // Touch TabHostView to populate contents of the inital tab page body.
            LanesNamesTabHostView.SelectedIndex = -1;
            LanesNamesTabHostView.SelectedIndex = 0;
        }
    }

balintn22 avatar Jan 08 '23 14:01 balintn22

I know it's been a long time since you opened this issue, but I've had success with BindableLayouts for this scenario.

In your example you have a view model with two collections, HeaderTexts and ContentGroups, where you want the tabs in the TabHostView to bind to the HeaderTexts objects and the ViewSwitcher to ContentGroups.

Here's how you could use a BindableLayout to create those views in the ViewSwitcher instead of doing it in code behind:

        <tabs:TabHostView
            x:Name="MyTabHostView"
            Grid.Row="0"
            HeightRequest="50"
            SelectedIndex="1"
            ShowScrollbar="False"
            TabType="Scrollable"
            ItemsSource="{Binding HeaderTexts}"
            ItemTemplate="{StaticResource HeaderTemplate}">
        </tabs:TabHostView>

        <tabs:ViewSwitcher
            x:Name="MyViewSwitcher"
            Grid.Row="1"
            Margin="16,0"
            VerticalOptions="StartAndExpand"
            Animate="False"
            SelectedIndex="{Binding Source={x:Reference MyTabHostView}, Path=SelectedIndex, Mode=OneWay}"
            BindableLayout.ItemsSource="{Binding ContentGroups}">
            
            <BindableLayout.ItemTemplate>
                <DataTemplate>
                    <Label Style={StaticResource LabelDescription} Text="{Binding Description}" />
                </DataTemplate>
            </BindableLayout.ItemTemplate>

        </tabs:ViewSwitcher>
    </Grid>

Adam-- avatar May 10 '24 19:05 Adam--