I\'m using StackLayout and ListView to show some part of a view, but the ListView takes more space than I need, and leaves a blank space between the last row of the list and
As hvaughan3 said it's really not a good practice. But I had the same case and I used workaround with behavior:
public class ListViewHeightBehavior : Behavior<ListView>
{
private ListView _listView;
public static readonly BindableProperty ExtraSpaceProperty =
BindableProperty.Create(nameof(ExtraSpace),
typeof(double),
typeof(ListViewHeightBehavior),
0d);
public double ExtraSpace
{
get { return (double)GetValue(ExtraSpaceProperty); }
set { SetValue(ExtraSpaceProperty, value); }
}
protected override void OnAttachedTo(ListView bindable)
{
base.OnAttachedTo(bindable);
_listView = bindable;
_listView.PropertyChanged += (s, args) =>
{
var count = _listView.ItemsSource?.Count();
if (args.PropertyName == nameof(_listView.ItemsSource)
&& count.HasValue
&& count.Value > 0)
{
_listView.HeightRequest = _listView.RowHeight * count.Value + ExtraSpace;
}
};
}
}
XAML:
<ListView ItemsSource="{Binding Items}"
HasUnevenRows="True">
<ListView.Behaviors>
<behaviors:ListViewHeightBehavior ExtraSpace="180"/>
</ListView.Behaviors>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
// Your template
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
First, Xamarin suggests not putting a ListView
in a ScrollView
, since they both offer scrolling and fight with each other. I have personally tried this and it can often cause weird behavior, especially on Android.
Instead, what I do is use the ListView.Header
with the ListView.HeaderTemplate
and the ListView.Footer
with the ListView.FooterTemplate
. Xamarin link here This allows for your exact scenario but without the weird behavior issues. If you would like to see an example of that, let me know.
If you must must must go with your current layout, you can try to check out this post talking about how the ListView
needs to be sized with specific Height
and Width
or with LayoutOptions
since it is not supposed to fit the size of it's contents. There are a couple solutions in that post, none that I have tried yet.
In this post one of the workarounds that is given, says to add the the ListView
to a StackLayout
and add that to another StackLayout
. Then set the inner StackLayout
's VerticalOptions
to LayoutOptions.FillAndExpand
.
So it might look like this:
<ScrollView>
<StackLayout Orientation="Vertical" Spacing="10">
....
<StackLayout VerticalOptions="FillAndExpand">
<ListView ItemsSource="{Binding Establishment.Contacts}" VerticalOptions="Start" HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding Value}" Detail="{Binding StringType}" DetailColor="Gray"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
....
</StackLayout>
</ScrollView>
Thank you guys, but nothing worked for me. The solution that I thought by myself surely isn't the best one, but it works for me.....
At the XAML I left an empty StackLayout like this:
<StackLayout x:Name="ContactsList" Orientation="Vertical" Spacing="10" Padding="8,0">
<!-- Empty because it will be filled in code mode -->
</StackLayout>
And then, in the .cs file, I called this method after InitializeComponent() method:
private void setUpContacts(Establishment establishment)
{
foreach(Contact contact in establishment.Contacts)
{
StackLayout row = new StackLayout
{
Orientation = StackOrientation.Vertical
};
row.Children.Add(new Label
{
TextColor = Color.Gray,
Text = string.Format("{0}:", contact.StringType)
});
row.Children.Add(new Label
{
TextColor = Color.Black,
FontAttributes = FontAttributes.Bold,
Text = contact.Value,
});
row.GestureRecognizers.Add(new TapGestureRecognizer {
Command = new Command(() => OnContactTapped(row, contact))
});
ContactsList.Children.Add(row);
}
}
I made this because the list isn't a really ListView, since I don't need scrolling functions directly in the list, it is just another part of the view. It won't cause any performance problem, I think, because it will have a small number of items (maximum 5). I'm marking this one as the correct answer to help others with this problem.
Please, if this is a solution that does not work for others, let me know and I'll find another way to do this.
I had issues with Yehor Hromadskyi's solution.
Here are the changes to the code to fix those issues
public class ListViewHeightBehavior : Behavior<ListView>
{
private ListView _listView;
public static readonly BindableProperty ExtraSpaceProperty =
BindableProperty.Create(nameof(ExtraSpace),
typeof(double),
typeof(ListViewHeightBehavior),
0d);
public static readonly BindableProperty DefaultRowHeightProperty =
BindableProperty.Create(nameof(DefaultRowHeight),
typeof(int),
typeof(ListViewHeightBehavior),
40);
public int DefaultRowHeight
{
get => (int)GetValue(DefaultRowHeightProperty);
set => SetValue(DefaultRowHeightProperty, value);
}
public double ExtraSpace
{
get { return (double)GetValue(ExtraSpaceProperty); }
set { SetValue(ExtraSpaceProperty, value); }
}
protected override void OnAttachedTo(ListView bindable)
{
base.OnAttachedTo(bindable);
_listView = bindable;
_listView.PropertyChanged += (s, args) =>
{
var count = _listView.ItemsSource?.Cast<object>()?.Count();
if (args.PropertyName == nameof(_listView.ItemsSource)
&& count.HasValue
&& count.Value > 0)
{
int rowHeight = _listView.RowHeight > 0 ? _listView.RowHeight : DefaultRowHeight;
_listView.HeightRequest = rowHeight * count.Value + ExtraSpace;
}
};
}
}
Sample layout shows that, you are using ListView inside the ScrollView and ListView have child in different height (HasUnevenRows="True")
<ScrollView>
<ListView HasUnevenRows="True">
</ListView>
</ScrollView>
This cause a blank space between the last row of the list and ScrollView.
Since you are already added scroll view as root. You can use StackLayout
(BindableLayout)
for the same purpose.
<ScrollView>
<StackLayout
x:Name="DummyList"
BindableLayout.ItemsSource="{Binding ContactList}"
Orientation="Vertical">
<BindableLayout.ItemTemplate>
<DataTemplate>
<TextCell
Detail="{Binding StringType}"
DetailColor="Gray"
Text="{Binding Value}" />
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</ScrollView>