'Any idea how to add CarouselView indicators inside the CarouselView it self?

I am using a CarouselView to display a few views

each one of them is basically 2 StackLayouts like so

enter image description here

what I have done so far is create a CarouselView with a template for both of these Stacklayouts

 <CarouselView
                    x:Name="MyCarouselView"
                    IsVisible="{Binding ShouldShow}" 
                    ItemsSource="{Binding MyItemSource}"
                    Scrolled="CarouselView_Scrolled">
                    <CarouselView.ItemTemplate>
                        <DataTemplate>
                            <ContentView>
                                <FlexLayout Direction="Column" JustifyContent="SpaceBetween">
                                    <StackLayout FlexLayout.Basis="25%">
                                        <elements:TopView Data="{Binding}"/>
                                    </StackLayout>

                                    <StackLayout FlexLayout.Basis="75%">
                                        <elements:BottomView Data="{Binding}"/>
                                    </StackLayout>
                                </FlexLayout>
                            </ContentView>
                        </DataTemplate>
                    </CarouselView.ItemTemplate>
                </CarouselView>

Note: the views bind to different values but for simplicity sake, I wrote a simpler DataTemplate here.

it works fine and there is no problem in binding or anything

what I am trying to do is add indicators to show the user that there are more items and which one he is seeing now. (small circles or something like that).

the thing is I want to display them between the two views like so

enter image description here

Any idea of how to do it? Any other way to achieve the wanted result is welcome as well

Please and thank you.



Solution 1:[1]

The easy way to implement that is using Grid to do that :

<Grid>
    <!-- Place new controls here -->
    <CarouselView x:Name="CustomCarouselView"
                  IndicatorView="indicatorView"
                  VerticalOptions="FillAndExpand"
                  >
        <CarouselView.ItemTemplate>
            <DataTemplate>
                <StackLayout>
                    <ContentView BackgroundColor="LightBlue" >
                        <Label Text="{Binding TopViewTitle}" FontSize="Header" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" HeightRequest="250"/>
                    </ContentView>
                    <ContentView BackgroundColor="LightGray" Margin="0,80,0,0">
                        <Label Text="{Binding BottomViewTitle}" FontSize="Header" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" HeightRequest="250" />
                    </ContentView>   
                </StackLayout>
            </DataTemplate>
        </CarouselView.ItemTemplate>
    </CarouselView>
    <IndicatorView x:Name="indicatorView"
                   IndicatorsShape="Square"
                   IndicatorColor="LightGray"
                   SelectedIndicatorColor="DarkGray"
                   VerticalOptions="Center"
                   Margin="0,-150,0,0" />
</Grid>

Above is my sample xaml code ,only need to custom the Margin property of IndicatorView and bottom ContentView after binding ItemSource for CarouselView :

List<CarouselModel> carouselModels = new List<CarouselModel>();
carouselModels.Add(new CarouselModel { TopViewTitle = "first top", BottomViewTitle = "first bottom" });
carouselModels.Add(new CarouselModel { TopViewTitle = "second top", BottomViewTitle = "second bottom" });
carouselModels.Add(new CarouselModel { TopViewTitle = "third top", BottomViewTitle = "third bottom" });

CustomCarouselView.ItemsSource = carouselModels;

The effect shows as expected :

enter image description here

The another way you can use RelativeLayout to implement that :

<RelativeLayout>
    <!-- Place new controls here -->
    <CarouselView x:Name="CustomCarouselView"
                    IndicatorView="indicatorView"
                    >
        <CarouselView.ItemTemplate>
            <DataTemplate>
                <StackLayout>
                    <ContentView BackgroundColor="LightBlue">
                        <Label Text="{Binding TopViewTitle}"
                                FontSize="Header"
                                HorizontalTextAlignment="Center"
                                VerticalTextAlignment="Center"
                                HeightRequest="250" />
                    </ContentView>
                    <ContentView BackgroundColor="LightGray"
                                    Margin="0,80,0,0">
                        <Label Text="{Binding BottomViewTitle}"
                                FontSize="Header"
                                HorizontalTextAlignment="Center"
                                VerticalTextAlignment="Center"
                                HeightRequest="250" />
                    </ContentView>
                </StackLayout>
            </DataTemplate>
        </CarouselView.ItemTemplate>
    </CarouselView>
    <IndicatorView x:Name="indicatorView"
                    IndicatorsShape="Square"
                    IndicatorColor="LightGray"
                    SelectedIndicatorColor="DarkGray"
                    RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent,Property=Width,Factor=0.5,Constant=-25}"
                    RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent,Property=Height,Factor=0.5,Constant=-75}"
                    />
</RelativeLayout>

Here you can modify RelativeLayout.XConstraint and RelativeLayout.YConstraint of IndicatorView to set the space between two ContentView . The Margin property of Bottom ContentView also need to set , it's effect is to show the space between two ConentView .

Solution 2:[2]

You can achieve this in two methods :-

  1. Grid, where you can place both the controls in the same Grid.Row. Assign VerticalOptions="FillAndExpand" to the CarouselView and VerticalOptions="Center" to the IndicatorView.
  2. By Using AbsoluteLayout.

Let me know if you have more queries.

Solution 3:[3]

We can achieve this scenario by placing a carousel view inside the parent carousel view and providing the Indicator view for the carousel placed inside. Also we need to bind the "Position" property of inside carousel view properly, so that indicator view points to the proper selected index. Please find the code below for reference,

Inside Xaml:

    <StackLayout Padding="30">
    <CarouselView x:Name="MyCarouselView"
                  ItemsSource="{Binding MyItemSource}" 
                  FlowDirection="MatchParent">
        
        <CarouselView.ItemTemplate>
            <DataTemplate>
                <ContentView>
                    
                    <FlexLayout Direction="Column" 
                                JustifyContent="SpaceBetween" 
                                BackgroundColor="Pink">
                        
                        <StackLayout FlexLayout.Basis="5%">
                            <BoxView HeightRequest="100" 
                                     WidthRequest="100" 
                                     BackgroundColor="{Binding Boxview1Color}" />
                        </StackLayout>

                        <CarouselView FlexLayout.Basis="0%" 
                                      IndicatorView="indicatorView" 
                                      IsSwipeEnabled="False"
                                      ItemsSource="{Binding Items}" 
                                      Position="{Binding Position}">
                            
                            <CarouselView.ItemTemplate>
                                <DataTemplate>
                                    <Label Text="{Binding ItemText}"/>
                                </DataTemplate>
                            </CarouselView.ItemTemplate>
                            
                        </CarouselView>
                        
                        <IndicatorView x:Name="indicatorView" 
                                       FlexLayout.Basis="10%" 
                                       HeightRequest="10" 
                                       IndicatorsShape="Circle" 
                                       IndicatorColor="White"  
                                       SelectedIndicatorColor="DarkBlue">

                        </IndicatorView>

                        <StackLayout FlexLayout.Basis="60%">
                            <BoxView HeightRequest="100" 
                                     WidthRequest="100" 
                                     BackgroundColor="{Binding Boxview2Color}"/>
                        </StackLayout>
                    </FlexLayout>
                </ContentView>
            </DataTemplate>
        </CarouselView.ItemTemplate>
    </CarouselView>
    
</StackLayout>

Inside ViewModel:

public class MainPageViewModel
{
    private ObservableCollection<MyItemSourceViewModel> _myItemSource;
    public ObservableCollection<MyItemSourceViewModel> MyItemSource { get; set; }
    
    public MainPageViewModel()
    {
        MyItemSource = new ObservableCollection<MyItemSourceViewModel>();
        MyItemSource.Add(new MyItemSourceViewModel { Boxview1Color = Color.Red, Boxview2Color = Color.Blue, Position = 0 });
        MyItemSource.Add(new MyItemSourceViewModel { Boxview1Color = Color.Green, Boxview2Color = Color.Purple, Position = 1});
        MyItemSource.Add(new MyItemSourceViewModel { Boxview1Color = Color.PeachPuff, Boxview2Color = Color.Brown, Position = 2 });

        var count = MyItemSource.Count;

        foreach(var item in MyItemSource)
        {
            var itemCount = count;

            var i = 0;
            item.Items = new List<ItemsModel>();

            while(itemCount > 0)
            {
                var itemModel = new ItemsModel { ItemText = i.ToString()};
                item.Items.Add(itemModel);
                i++;
                itemCount--;
            }
        }
    }
}

public class MyItemSourceViewModel
{
    public Color Boxview1Color { get; set; }
    public Color Boxview2Color { get; set; }
    public int Position { get; set; }
    public List<ItemsModel> Items { get; set; }

}

public class ItemsModel
{
    public string ItemText { get; set; }
}

Please refer below screenshots as well:

Sample screenshot 1 Sample screenshot 2

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Junior Jiang
Solution 2 Nikhil
Solution 3