I am stuck on creating a Listview
that supports horizontal and vertical scrolling, like this:
I don't understand how to achieve this type of ListView
in Xamarin forms.
Scrolling contentSet the Content property of ScrollView to the view you want to scroll. This is often a StackLayout , but it can be any view. Set the Orientation property of ScrollView to a member of the ScrollOrientation property, Vertical , Horizontal , or Both . The default is Vertical .
To enable horizontal scrolling, we can use the CSS property overflow-x. If we assign the value scroll to the overflow-x property of the container element, the browser will hide horizontally overflowing content and make it accessible via horizontal scrolling.
In Android, a ScrollView is a view group that is used to make vertically scrollable views. A scroll view contains a single direct child only. In order to place multiple views in the scroll view, one needs to make a view group(like LinearLayout) as a direct child and then we can define many views inside it.
You can achieve Horizontal & Vertical Listview by Custom Control. Kindly Refer this Code,
1) Extend Scrollview Properties for renderized Listview
using System;
using System.Collections;
using System.Collections.Generic;
using System.Windows.Input;
using Xamarin.Forms;
namespace ProjectName.CustomControls
{
public class HorizontalListview : ScrollView
{
public static readonly BindableProperty ItemsSourceProperty =
BindableProperty.Create("ItemsSource", typeof(IEnumerable), typeof(HorizontalListview), default(IEnumerable));
public IEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly BindableProperty ItemTemplateProperty =
BindableProperty.Create("ItemTemplate", typeof(DataTemplate), typeof(HorizontalListview), default(DataTemplate));
public DataTemplate ItemTemplate
{
get { return (DataTemplate)GetValue(ItemTemplateProperty); }
set { SetValue(ItemTemplateProperty, value); }
}
public event EventHandler<ItemTappedEventArgs> ItemSelected;
public static readonly BindableProperty SelectedCommandProperty =
BindableProperty.Create("SelectedCommand", typeof(ICommand), typeof(HorizontalListview), null);
public ICommand SelectedCommand
{
get { return (ICommand)GetValue(SelectedCommandProperty); }
set { SetValue(SelectedCommandProperty, value); }
}
public static readonly BindableProperty SelectedCommandParameterProperty =
BindableProperty.Create("SelectedCommandParameter", typeof(object), typeof(HorizontalListview), null);
public object SelectedCommandParameter
{
get { return GetValue(SelectedCommandParameterProperty); }
set { SetValue(SelectedCommandParameterProperty, value); }
}
public void Render()
{
if (ItemTemplate == null || ItemsSource == null)
return;
var layout = new StackLayout();
layout.Spacing = 0;
layout.Orientation = Orientation == ScrollOrientation.Vertical ? StackOrientation.Vertical : StackOrientation.Horizontal;
foreach (var item in ItemsSource)
{
var command = SelectedCommand ?? new Command((obj) =>
{
var args = new ItemTappedEventArgs(ItemsSource, item);
ItemSelected?.Invoke(this, args);
});
var commandParameter = SelectedCommandParameter ?? item;
var viewCell = ItemTemplate.CreateContent() as ViewCell;
viewCell.View.BindingContext = item;
viewCell.View.GestureRecognizers.Add(new TapGestureRecognizer
{
Command = command,
CommandParameter = commandParameter,
NumberOfTapsRequired = 1
});
layout.Children.Add(viewCell.View);
}
Content = layout;
}
}
2) Include Namespace in your XAML Page
xmlns:control="clr-namespace:ProjectName"
3) Use Control in your Design
<control:HorizontalListview Orientation="Horizontal" x:Name="lst">
<control:HorizontalListview.ItemTemplate>
<DataTemplate>
<ViewCell>
<Label Style="{StaticResource lblPlaceNumberRing}" />
</ViewCell>
</DataTemplate>
</control:HorizontalListview.ItemTemplate>
</control:HorizontalListview>
4) Make Renderer for Android
using System;
using System.ComponentModel;
using ProjectName;
using ProjectName.Droid;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(HorizontalListview), typeof(HorizontalListviewRendererAndroid))]
namespace ProjectName.Droid.Renderers
{
public class HorizontalListviewRendererAndroid : ScrollViewRenderer
{
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
var element = e.NewElement as HorizontalListview;
element?.Render();
if (e.OldElement != null)
e.OldElement.PropertyChanged -= OnElementPropertyChanged;
e.NewElement.PropertyChanged += OnElementPropertyChanged;
}
protected void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (ChildCount > 0)
{
GetChildAt(0).HorizontalScrollBarEnabled = false;
GetChildAt(0).VerticalScrollBarEnabled = false;
}
}
}
}
5) Make Renderer for iOS
using System;
using UIKit;
using ProjectName.iOS;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using ProjectName;
using System.ComponentModel;
[assembly: ExportRenderer(typeof(HorizontalListview), typeof(HorizontalListviewRendererIos))]
namespace ProjectName.iOS.Renderers
{
public class HorizontalListviewRendererIos : ScrollViewRenderer
{
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
var element = e.NewElement as HorizontalListview;
element?.Render();
if (e.OldElement != null)
e.OldElement.PropertyChanged -= OnElementPropertyChanged;
e.NewElement.PropertyChanged += OnElementPropertyChanged;
}
protected void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
this.ShowsHorizontalScrollIndicator = false;
this.ShowsVerticalScrollIndicator = false;
this.AlwaysBounceHorizontal = false;
this.AlwaysBounceVertical = false;
this.Bounces = false;
}
}
}
I tried to just have a ScrollView
around the ListView
but that causes unpleasant behaviour if the ListView
is responsible for the vertical scrolling while the ScrollView
does the horizontal scrolling. So you have to ensure that the ListView
doesn't scroll in any direction and the ScrollView
does in both directions.
The latter is is easy to achive by just setting:
scrollView.Orientation = ScrollOrientation.Both;
Preventing the scrolling of the ListView
is a bit more tricky. Apparently the only way to do it is to ensure that the ListView.HeightRequest
is greater than it actually needs. I ended up doing it like this:
listView.HeightRequest = listView.RowHeight * (ListUsedAsItemSource.Count + 1) + 1;
The + 1
inside the brackets is to set a bit of height for the headers too. If you don't have headers you can omit it. Also be aware that you will have to set the listView.RowHeight
manually (the default value is 40 I think). In my case without setting it on my own it was -1.
As a ListView
doesn't scroll horizontally you don't have to prevent anything here. But per default the ListView
will fit to the horizontal space. So you also have to set the WidthRequest
similar to the HeightRequest
above. Anyways, you won't be able to get something like a column width from the ListView
so you'll have define a value on your own (and also use that value as the WidthRequest
of your columns)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With