Reputation: 4210
I'd like to have a Xamarin.Forms ListView that starts showing its elements from the bottom up, so that elements are displayed at the bottom of the ListView when there are few items. See the image for explanation. Is this possible to do without having to set the HeightRequest of the ListView manually? (And if setting the HeightRequest manually is required, how can I compute the height of the ListView's child elements when I'm using HasUnevenRows=true
?)
Here's how I want it to look (few items to the left, many items to the right);
For reference, here is the way ListView normally works when there are few items:
Upvotes: 3
Views: 1678
Reputation: 13601
If ListView
is not mandatory, and you just need a repeatable control with templating support - you can use ItemsControl
. As it uses StackLayout
as its panel, it only expands to its required height. So, you don't need to compute it's height. Please do note ItemsControl
has very basic virtualization behavior (recycling) - so if you have a lot of items then you will have to modify the control to support virtualization too.
Secondly, you can extend Grid
to automatically set the second RowDefinition
to Auto
or Star
as per bottom content size. i.e. by default keep it as Auto
, but if the content size is greater than 50% of viewport height - then reset RowDefinition
to Star
. For eg, you can create a custom SplitterGrid
as:
public class SplitterGrid : Grid
{
public SplitterGrid()
{
RowDefinitions = new RowDefinitionCollection()
{
new RowDefinition { Height = GridLength.Star },
new RowDefinition { Height = GridLength.Auto }
};
}
void Content2_SizeChanged(object sender, EventArgs e)
{
if (Height == 0 && Width == 0)
return;
Content2.SizeChanged -= Content2_SizeChanged;
if (Content2.Height > Height / 2)
RowDefinitions[1].Height = GridLength.Star;
else
RowDefinitions[1].Height = GridLength.Auto;
}
public static readonly BindableProperty Content1Property =
BindableProperty.Create(
"Content1", typeof(View), typeof(SplitterGrid),
defaultValue: null, propertyChanged: OnContent1Changed);
public View Content1
{
get { return (View)GetValue(Content1Property); }
set { SetValue(Content1Property, value); }
}
private static void OnContent1Changed(BindableObject bindable, object oldValue, object newValue)
{
((SplitterGrid)bindable).OnContent1ChangedImpl((View)oldValue, (View)newValue);
}
void OnContent1ChangedImpl(View oldValue, View newValue)
{
if (oldValue == null)
Children.Add(Content1);
}
public static readonly BindableProperty Content2Property =
BindableProperty.Create(
"Content2", typeof(View), typeof(SplitterGrid),
defaultValue: null, propertyChanged: OnContent2Changed);
public View Content2
{
get { return (View)GetValue(Content2Property); }
set { SetValue(Content2Property, value); }
}
private static void OnContent2Changed(BindableObject bindable, object oldValue, object newValue)
{
((SplitterGrid)bindable).OnContent2ChangedImpl((View)oldValue, (View)newValue);
}
void OnContent2ChangedImpl(View oldValue, View newValue)
{
if (oldValue == null)
{
Children.Add(Content2);
Content2.SizeChanged += Content2_SizeChanged;
Grid.SetRow(Content2, 1);
}
else
Content2.SizeChanged -= Content2_SizeChanged;
}
}
And, usage would look like:
<local:SplitterGrid Margin="0,20,0,0">
<local:SplitterGrid.Content1>
<ContentView Padding="10" BackgroundColor="#E4E4E4">
<Label Text="This page contains about 3 items." />
</ContentView>
</local:SplitterGrid.Content1>
<local:SplitterGrid.Content2>
<ScrollView VerticalOptions="End">
<local:ItemsControl>
<local:ItemsControl.ItemTemplate>
<DataTemplate>
<Label FontAttributes="Bold" FontSize="40" Text="{Binding .}" />
</DataTemplate>
</local:ItemsControl.ItemTemplate>
<local:ItemsControl.ItemsSource>
<x:Array Type="{x:Type x:String}">
<x:String>Item 1</x:String>
<x:String>Item 2</x:String>
<x:String>Item 3</x:String>
</x:Array>
</local:ItemsControl.ItemsSource>
</local:ItemsControl>
</ScrollView>
</local:SplitterGrid.Content2>
</local:SplitterGrid>
Upvotes: 3
Reputation: 888
I would go with a grid where two rows are defined with one set to "*" or the Height you want in pixels and the bottom one to "Auto" with the listview VerticalOptions set to "End".
Upvotes: 0