36
votes

Is any way to create ListView with horizontal scroll in Xamarin.Forms like image

ListView Horizontal

this is what i have done for vertical

var myListView = new ListView
{
    ItemTemplate = new DataTemplate(typeof(ImageCell))
};
14
I need the same but wants in Xamarin AndroidAjay Sharma
The proper way to do this is to use a Carousel View, as explained below by Korayem: stackoverflow.com/a/37245057/5953643. Xamarin.Forms doesn't support Horizontal Scrolling in a ListView. You can create one using the recommendations below, but they are unsupported and considered "hacks", and these unsupported "hacks" are likely to break in future updates to Xamarin.Forms.Brandon Minnick
@BrandonMinnick - AFAIK, Carousel View only supports swiping to move over one item at a time. That is a different use case than list view, which allows swiping past many items at once. See Ricardo's answer for an option that looks more useful (I have not tried).ToolmakerSteve
Read my answer in this similar post: stackoverflow.com/a/55918563/10455239Dev

14 Answers

17
votes

As of Xamarin Forms 2.3 CarouselView does just that, and more. Read more here.

<ContentView HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
  <CarouselView ItemsSource="{Binding MyDataSource}">
    <CarouselView.ItemTemplate>
      <DataTemplate>
        <Label Text="{Binding LabelText}" />
      </DataTemplate>
    </CarouselView.ItemTemplate>
  </CarouselView>
</ContentView>
12
votes

Yes, you technically can. Set the Rotation to 270 (all VisualElements have a Rotation BindableProperty). However, this looks like a suboptimal solution as there are white spaces at the top and bottom and you have to drag the view left and right to see everything fully.

public static readonly BindableProperty RotationProperty;
public static readonly BindableProperty RotationXProperty;
public static readonly BindableProperty RotationYProperty;

The code above is from the VisualElement class. The code below is a small sample of my own.

                                              ∨∨∨                                                  
<ListView x:Name="MessagesListView" Rotation="270" ItemsSource="{Binding Items}" RowHeight="40">
  <ListView.ItemTemplate>
    <DataTemplate>
      <ViewCell>
        <ViewCell.View>
          <StackLayout>
            <!--mylayouthere-->
          </StackLayout>
        </ViewCell.View>
      </ViewCell>
    </DataTemplate>
  </ListView.ItemTemplate>
</ListView>
11
votes

As everyone else has said, No - there isn't one available out of the box in Xamarin.Forms.

However - it doesn't stop anyone from writing there own custom renderer to achieve this type of control.

As Stephane Delcroix has mentioned, you can create a ScrollView and then a StackLayout as a child to create the same effect.

You will then need to implement:-

*) bindable property to accept the (IEnumerable) ItemsSource property that needs creating.

*) bindable property to accept the (DataTemplate) ItemTemplate property that needs creating.

*) binding code to instantiate instances of the ItemTemplate taking the specific datasource item and rendering this into the StackLayout. Your have to consider items removed etc also.

*) attach event handlers / tap gestures for item selection.

*) implementing a selected state / deactivating other selected items.

... and so on to get a full implementation.

The problem with all of the above is that it is fine for relatively small item lists.

However, if you are looking for a long list of entries, then above would be a little undesirable as you are creating all of the Views upfront.

Even if you delayed loading of these, you still have the memory footprint of all the Views to consider.

This then leads onto another possible implementation that deals with Virtualized Items, which is a whole different story to consider.

10
votes

As pointed out above, there is no standard way of doing this, however there is a way around it using a standard ListView and @MillieSmiths approach.

The solution needs several layers of nested layouts. Starting with the ListViewwe will rotate that 270 degrees, however that also rotates our item content, so we need to rotate that back by 90 degrees.

Rotating the ListView creates an awful lot of whitespace, by wrapping the ListView in an absolute layout we can solve that (we need an extra contentview in there to fix some clipping problems).

Finally in the codebehind we need to render the layout clipping

Behold the full solution:

<AbsoluteLayout x:Name="MessagesLayoutFrame" Padding="0" HorizontalOptions="FillAndExpand">
  <ContentView x:Name="MessagesLayoutFrameInner"  Padding="0"  HorizontalOptions="FillAndExpand">
    <ListView x:Name="MessagesListView"
              ItemsSource="{Binding Images}"
              RowHeight="240"
              VerticalOptions="Start"
              HeightRequest="240"
              WidthRequest="240"
              SeparatorVisibility="None"
              Rotation="270"
              HorizontalOptions="Center">
      <ListView.ItemTemplate>
        <DataTemplate>
          <ViewCell>
            <ContentView Rotation="90" Padding="12">
              <Image Source="{Binding Source}" Aspect="AspectFill" />
            </ContentView>
          </ViewCell>
        </DataTemplate>
      </ListView.ItemTemplate>
    </ListView>
  </ContentView>
</AbsoluteLayout>

For the code behind we just need to check if we have set things up before, if we have, let it go. Basically we are finding out what the width of the page is (NameGrid is just a full width container somewhere else) then moving the direct ListView container up by half the whitespace, and clipping it by the other half on the bottom)

    bool hasAppearedOnce = false;
    protected override void OnAppearing() {
        base.OnAppearing();

        if (!hasAppearedOnce) {

            hasAppearedOnce = true;
            var padding = (NameGrid.Width - MessagesListView.Height) / 2;

            MessagesListView.HeightRequest = MessagesLayoutFrame.Width;
            MessagesLayoutFrameInner.WidthRequest = MessagesLayoutFrame.Width;
            MessagesLayoutFrameInner.Padding = new Thickness(0);
            MessagesLayoutFrame.Padding = new Thickness(0);
            MessagesLayoutFrame.IsClippedToBounds = true;
            Xamarin.Forms.AbsoluteLayout.SetLayoutBounds(MessagesLayoutFrameInner, new Rectangle(0, 0 - padding, AbsoluteLayout.AutoSize, MessagesListView.Height - padding));
            MessagesLayoutFrameInner.IsClippedToBounds = true;
             // */
        } 
    }

WARNING DO NOT USE <FRAMES> for the layout moving and rotating. It will crash on Windows Phone.

P.S I am sure this could be wrapped up in a nice UserControl for everyone to use.

8
votes

In Xamarin.Forms 4.0-pre you can use the CollectionView, which simplifies getting this type of layout.

Sample:

<CollectionView ItemsSource="{Binding Monkeys}">
    <CollectionView.ItemsLayout>
        <ListItemsLayout>
            <x:Arguments>
                <ItemsLayoutOrientation>Horizontal</ItemsLayoutOrientation>    
            </x:Arguments>
        </ListItemsLayout>
    </CollectionView.ItemsLayout>
</CollectionView>
7
votes

Like the others have said, not possible with ListView and I think it's a big oversight by Xamarin with Forms. We need to dynamically display data driven objects in more than just a list doing down....come on ya'll!!

However, in the Xamarin Labs project there is GridView which you could use. It's still a bit rough and folks are working through some bugs now with selecting the items.

https://github.com/XForms/Xamarin-Forms-Labs

Someone does seem to have a work around for that problem:

https://github.com/XForms/Xamarin-Forms-Labs/issues/236

6
votes

No, there's no way to have an horizontal ListView. You can wrap an horizontal StackLayout in an horizontal ScrollView to achieve the same visual result, but that's not quite the same, as you won't have DataTemplating.

3
votes
3
votes

This nuget package will fit perfectly for your case. I have used this one before and I really like it:

https://github.com/SuavePirate/DynamicStackLayout

To make things even better download these 3 Nuget packages to have an image loading, caching & transformation on your photos. The photos will be shaped in a circle but this nuget has other types of transformations:

Xamarin.FFImageLoading (https://github.com/luberda-molinet/FFImageLoading/wiki/Xamarin.Forms-API)
Xamarin.FFImageLoading.Forms
Xamarin.FFImageLoading.Transformations (https://github.com/luberda-molinet/FFImageLoading/wiki/Transformations-Guide)

Here is a piece of code to help you to start:

<!--Add this code to the top of your page-->
xmlns:ffimageloading="clr-namespace:FFImageLoading.Forms;assembly=FFImageLoading.Forms"
xmlns:fftransformations="clr-namespace:FFImageLoading.Transformations;assembly=FFImageLoading.Transformations"
xmlns:dynamicStackLayout="clr-namespace:SuaveControls.DynamicStackLayout;assembly=SuaveControls.DynamicStackLayout"


<!-- Here is your control inside a ScrollView. The property Photos is a list of images address (Urls)  -->

<ScrollView Orientation="Horizontal" HorizontalOptions="FillAndExpand">
    <dynamicStackLayout:DynamicStackLayout ItemsSource="{Binding Photos}" HorizontalOptions="Fill" Orientation="Horizontal" Padding="10, -0, 100, 10">
        <dynamicStackLayout:DynamicStackLayout.ItemTemplate>
            <DataTemplate>
                <StackLayout BackgroundColor="Transparent" >
                    <ffimageloading:CachedImage HorizontalOptions="Start" VerticalOptions="Center" DownsampleToViewSize="true" Aspect="AspectFit" Source="{Binding .}">
                        <ffimageloading:CachedImage.GestureRecognizers>
                            <TapGestureRecognizer Command="{Binding Path=PhotoCommand}" CommandParameter="{Binding .}" NumberOfTapsRequired="1" />
                        </ffimageloading:CachedImage.GestureRecognizers>
                        <ffimageloading:CachedImage.HeightRequest>
                            <OnPlatform x:TypeArguments="x:Double">
                                <On Platform="iOS" Value="50" />
                                <On Platform="Android" Value="60" />
                            </OnPlatform>
                        </ffimageloading:CachedImage.HeightRequest>
                        <ffimageloading:CachedImage.WidthRequest>
                            <OnPlatform x:TypeArguments="x:Double">
                                <On Platform="iOS" Value="50" />
                                <On Platform="Android" Value="60" />
                            </OnPlatform>
                        </ffimageloading:CachedImage.WidthRequest>
                        <ffimageloading:CachedImage.Transformations>
                            <fftransformations:CircleTransformation BorderHexColor="#eeeeee">
                                <fftransformations:CircleTransformation.BorderSize>
                                    <OnPlatform x:TypeArguments="x:Double">
                                        <On Platform="iOS" Value="10" />
                                        <On Platform="Android" Value="10" />
                                    </OnPlatform>
                                </fftransformations:CircleTransformation.BorderSize>
                            </fftransformations:CircleTransformation>
                        </ffimageloading:CachedImage.Transformations>
                    </ffimageloading:CachedImage>
                </StackLayout>
            </DataTemplate>
        </dynamicStackLayout:DynamicStackLayout.ItemTemplate>
    </dynamicStackLayout:DynamicStackLayout>
</ScrollView>

I hope it helps :)

2
votes

I have tried the mentioned "rotating" solution and besides it being an "ugly" solution, it also comes with several limitations:

  1. list view WidthRequest has to be the same as HeightRequest
  2. listView row height does not work properly anymore because it becomes cell width
  3. verticalalign becomes horizontalalign etc, not very maintainable.

A better option is to make your own custom control or, like I did, use an existing HorizontalListView: https://www.nuget.org/packages/HorizontalListView1.1/ This one is easy to use. You can find the source code and documentation here

Implementation:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
    x:Class="test.ListPage" 
    xmlns:Controls="clr-namespace:HorizontalList;assembly=HorizontalList"> 

<Controls:HorizontalListView ItemsSource="{Binding Categories}" ListOrientation="Horizontal"> 
  <Controls:HorizontalListView.ItemTemplate> 
    <DataTemplate> 
    <Label Text="{Binding Name}" /> 
    </DataTemplate> 
  </Controls:HorizontalListView.ItemTemplate> 
  </Controls:HorizontalListView>
</ContentPage>
1
votes

Till the CollectionView is out, you can use my Xamarin.Forms HorizontalListView.

It has:

  • Snapping on first or middle element
  • Padding and item spacing
  • Handles NotifyCollectionChangedAction Add, Remove and Reset actions
  • View recycling
  • RecyclerView on Android
  • UICollectionView on iOS
  • This implementation is in fact very close in terms of philosophy and implementation to what will provide the future Xamarin CollectionView.
0
votes

As far as I know, there is 3 ways to implement this:

  1. Rotation (as mentioned by other guys)
    • No need to do anymore that standard ListView
    • ItemTemplate is available
    • Ugly solution!
  2. Custom Render (RecyclerView in Android and (I think) UICollectionView in iOS)
    • Custom cell is available (I'm sure about Android, but don't sure about iOS)
    • Needs more work and code
  3. Grid and Horizontal ScrollView (using horizontal as value for orientation prop in ScrollView )
    • Custom layout is available
    • There is no CachingStrategy available in this solution, so for huge list this may cause huge RAM usage for your app
0
votes

What about this: https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/collectionview/layout#horizontal-list

In XAML, a CollectionView can display its items in a horizontal list by setting its ItemsLayout property to HorizontalList:

<CollectionView ItemsSource="{Binding Monkeys}"
                ItemsLayout="HorizontalList">
    <CollectionView.ItemTemplate>
        <DataTemplate>
            <Grid Padding="10">
                <Grid.RowDefinitions>
                    <RowDefinition Height="35" />
                    <RowDefinition Height="35" />
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="70" />
                    <ColumnDefinition Width="140" />
                </Grid.ColumnDefinitions>
                <Image Grid.RowSpan="2"
                       Source="{Binding ImageUrl}"
                       Aspect="AspectFill"
                       HeightRequest="60"
                       WidthRequest="60" />
                <Label Grid.Column="1"
                       Text="{Binding Name}"
                       FontAttributes="Bold"
                       LineBreakMode="TailTruncation" />
                <Label Grid.Row="1"
                       Grid.Column="1"
                       Text="{Binding Location}"
                       LineBreakMode="TailTruncation"
                       FontAttributes="Italic"
                       VerticalOptions="End" />
            </Grid>
        </DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView>

Alternatively, this layout can also be accomplished by setting the ItemsLayout property to a LinearItemsLayout object, specifying the Horizontal ItemsLayoutOrientation enumeration member as the Orientation property value:

<CollectionView ItemsSource="{Binding Monkeys}">
    <CollectionView.ItemsLayout>
        <LinearItemsLayout Orientation="Horizontal" />
    </CollectionView.ItemsLayout>
    ...
</CollectionView>
-1
votes

This has been solved with a custom class called ItemsView (not to be confused with Xamarin's ItemsView for data templating in a ListView) which implements a ScrollView/StackPanel pattern mentioned above which example has already been requested for. Please see the code: https://gist.github.com/fonix232/b88412a976f67315f915