Can't get ListView to vertically center within StackLayout

I'm expecting the following listview to be centered vertically:
<StackLayout Orientation="Vertical"> <ListView ItemsSource="{Binding Children}" VerticalOptions="Center"> <ListView.ItemTemplate> <DataTemplate> <ViewCell> <Button Text="{Binding Name}" VerticalOptions="Center"/> </ViewCell> </DataTemplate> </ListView.ItemTemplate> </ListView> </StackLayout>

But instead it is at the top of the page.

However, if I use the Visual Studio live tree editor to set the ListView VerticalAlignment to Center, I get what I want. What do I need to change in the xaml to get the same behavior?

Answers

  • JohnHardmanJohnHardman GBUniversity mod

    @mattsloan - I'd start by setting the StackLayout to have VerticalOptions="FillAndExpand". That will give the ListView something to center itself in.

  • mattsloanmattsloan USMember
    edited July 2016

    That didn't seem to change anything. Here is my new complete xaml file:
    <?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms" prism:ViewModelLocator.AutowireViewModel="True" x:Class="ChoreBoard.ChildSelectPage"> <StackLayout Orientation="Vertical" VerticalOptions="FillAndExpand"> <ListView ItemsSource="{Binding Children}" VerticalOptions="Center"/> <ListView.ItemTemplate> <DataTemplate> <ViewCell> <Button Text="{Binding Name}" /> </ViewCell> </DataTemplate> </ListView.ItemTemplate> </ListView> </StackLayout> </ContentPage>

    Any other ideas? Based on the fact that simply changing the ListView at runtime to VerticalAlignment=Center fixes it, i'm concerned there is a bug with Xamarin.Forms here.

  • JohnHardmanJohnHardman GBUniversity mod

    Time for sleep now. I'll take a look tomorrow. There are occasions where I have found it necessary to add another Layout (e.g. another StackLayout) around a View to get the layout manager to do what you (well, I anyway) would expect it to do regardless. But, will check tomorrow.

  • PubuduGayanPubuduGayan NZMember ✭✭✭

    @mattsloan i am with @JohnHardman , i am also adding extra stack layout when i ma dealing with list view other wise its not behave what its promise, any way above practice always help me.

    like your comment @AlessandroCaliaro

  • mattsloanmattsloan USMember

    Extra stack layout did not help either. Here is my new xaml:
    <?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms" prism:ViewModelLocator.AutowireViewModel="True" x:Class="ChoreBoard.ChildSelectPage"> <StackLayout Orientation="Vertical" VerticalOptions="FillAndExpand"> <StackLayout Orientation="Vertical" VerticalOptions="Center"> <ListView ItemsSource="{Binding Children}" VerticalOptions="Center"> <ListView.ItemTemplate> <DataTemplate> <ViewCell> <Button Text="{Binding Name}" VerticalOptions="Center"/> </ViewCell> </DataTemplate> </ListView.ItemTemplate> </ListView> </StackLayout> </StackLayout> </ContentPage
    Any other ideas?

  • ashalvaashalva GEMember ✭✭✭

    Try
    <StackLayout Orientation="Vertical" VerticalOptions="CenterAndExpand">
    or
    <StackLayout Orientation="Vertical" VerticalOptions="Center">

  • JohnHardmanJohnHardman GBUniversity mod
    edited July 2016

    @mattsloan - I've spent a few minutes trying different things. It really doesn't behave the way I expected it to. Even if the ListView contains just one item, it stretches vertically to take as much space as it can. Whilst wrapped in a StackLayout, the only way I got it centered vertically without being centered by lieu of taking all available space was by specifying HeightRequest on the ListView.

    Based on this, depending on what appearance you are after, you might be ok with StackLayout and HeightRequest, or you might want to switch to an AbsoluteLayout, RelativeLayout or Grid.

    It's always worth setting BackgroundColor on Views when investigating stuff like this to help understand what is happening.

  • coolrjmcoolrjm INMember ✭✭✭

    Hi,

    Try putting a grid inside the stacklayout.

    <StackLayout Orientation="Vertical" VerticalOptions="Center" HorizontalOptions="Center"> <Grid RowSpacing="12" VerticalOptions="Center">

    And put your listview in one column of the Grid and set the column width to a specific value. That will make your listview vertically center within the stacklayout.

    Regards,

    Rajesh.

  • mattsloanmattsloan USMember
    edited August 2016

    @JohnHardman Thank you for the investigation. I really would like to know why simply setting the ListView to vertically center doesn't do what we expect though. This seems like a bug.

    @coolrjm - Did you try that yourself? Using a grid does not get any different behavior for me.

  • JohnHardmanJohnHardman GBUniversity mod

    @mattsloan - You could log it in bugzilla to see what Xamarin say, but I'm not sure it's really a bug. IMHO, expanding to take all the space it can is a reasonable default behavior for a ListView, as it's not obvious what alternative logic it could use. I don't recall seeing its current behavior documented - if the behavior is as intended, documenting it would be good :-)

  • mattsloanmattsloan USMember

    @JohnHardman - I'd agree that expanding is reasonable default behavior, but if it provides an option to set to vertically center, I would expect it to center.

    Also, the fact that I can set a field on the ListView at runtime to vertically align and get the desired behavior makes me think the VerticalOptions that are set in xaml are not mapping correctly to the alignment settings at runtime.

    I'm going to take a peek at the source code then probably submit a bug.

  • DavidDancyDavidDancy AUMember ✭✭✭✭

    @mattsloan have you tried putting filler objects above and below the ListView?

  • JohnHardmanJohnHardman GBUniversity mod

    @DavidDancy - I did but without great success. Constraining the height of the ListView by specifying a HeightRequest for the ListView does work. I would also expect containing/constraining the ListView inside a Grid, AbsoluteLayout or RelativeLayout, where the height of is defined by the container, to work as well.

    If you manage to get it working by using filler objects in a StackLayout, I'd be interested to see what I missed :-)

  • DavidDancyDavidDancy AUMember ✭✭✭✭

    @JohnHardman I've spent a few minutes trying to make a ListView centre itself inside a StackLayout and basically gave up. IMO the VerticalOptions and HorizontalOptions on the StackLayout do not actually do what I intuit that they should.

    In addition, there seems to be a pathological behaviour of ListView to auto-expand itself to fill its parent view regardless of how much space is actually needed to display its content.

    I find at present that the Grid is a much more reliable and intuitive control to use for layout, although it's obviously a bit more work to set up.

    To that end I created a sample XAML definition based on the original post in this thread and ran some experiments. My XAML has the ListView bracketed top and bottom by either BoxView or Label objects in an attempt to force the centring issue.

    Using StackLayout, the only way I could get the ListView to centre itself in this layout was to give the top and bottom padding elements a HeightRequest each. Leaving them to their own devices means that the ListView greedily grabs all available height and the other elements then sit at zero. This in turn makes it look like the ListView isn't centred (even though it technically is).

    So then I swapped the outer StackLayout for a Grid, but the same problem occurs there too. The ListView seems to get priority over all other screen elements when it comes to sizing, with the result that my top and bottom padding views also ended up at zero height.

    The only way I found to get the effect that the OP appears to be wanting is to override OnBindingContextChanged in the code-behind file and set the height of my middle Grid row to some non-zero value. This forces the ListView to not use the whole height of the parent control. In my example I was able to vary the height of the ListView according to its content, but this does not appear to be something the ListView can do all by itself.

    <?xml version="1.0" encoding="utf-8"?>
    <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
        xmlns:local="clr-namespace:ListViewCentered"
        x:Class="ListViewCentered.ListViewCenteredPage"
        Padding="0">
        <Grid x:Name="_pageGrid"
            Padding="0"
            RowSpacing="0">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="*" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="*" />
            </Grid.RowDefinitions>
            <Label x:Name="_topFiller"
                Grid.Column="0"
                Grid.Row="0"
                BackgroundColor="Olive"
                Text=""
                VerticalOptions="StartAndExpand" />
            <ListView x:Name="_children"
                Grid.Column="0"
                Grid.Row="1"
                BackgroundColor="Aqua"
                ItemsSource="{Binding Children}"
                SeparatorVisibility="None"
                VerticalOptions="Center">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <ViewCell>
                            <Grid HeightRequest="20"
                                Padding="0"
                                RowSpacing="0">
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="*" />
                                </Grid.ColumnDefinitions>
                                <Grid.RowDefinitions>
                                    <RowDefinition Height="*" />
                                    <RowDefinition Height="1" />
                                </Grid.RowDefinitions>
                                <Label Grid.Column="0"
                                    Grid.Row="0"
                                    Text="{Binding Name}"
                                    VerticalTextAlignment="Center"
                                    VerticalOptions="Center" />
                                <BoxView Grid.Column="0"
                                    Grid.Row="1"
                                    HeightRequest="1"
                                    VerticalOptions="End"
                                    BackgroundColor="Gray" />
                            </Grid>
                        </ViewCell>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
            <Label x:Name="_bottomFiller"
                Grid.Column="0"
                Grid.Row="2"
                Text=""
                BackgroundColor="Lime"
                VerticalOptions="EndAndExpand" />
        </Grid>
    </ContentPage>
    

    Code-behind:

    using System.Collections;
    using System.Collections.Generic;
    using Xamarin.Forms;
    using System.Linq;
    
    namespace ListViewCentered
    {
        public partial class ListViewCenteredPage : ContentPage
        {
            public ListViewCenteredPage ()
            {
                InitializeComponent ();
            }
    
            protected override void OnBindingContextChanged ()
            {
                base.OnBindingContextChanged ();
    
                var data = (Data)BindingContext;
    
                var height = new GridLength (data.Children.Count * 44);
                _pageGrid.RowDefinitions [1].Height = height;
            }
        }
    }
    
    
  • JohnHardmanJohnHardman GBUniversity mod

    @DavidDancy - Thanks for doing that investigation. Your observations tie in with what I saw, so glad that I hadn't missed something :-)

  • batmacibatmaci DEMember ✭✭✭✭✭
    edited August 2017

    @AlessandroCaliaro said:
    @JohnHardman Xamarin people never sleep ;)

    I thought it was only me :hushed:

  • NMackayNMackay GBInsider, University mod

    @DavidDancy

    Thanks for looking into this, I'm seeing exactly the same, it's not just the Xamarin vanilla listview, Telerik's RadList behaves the exact same, in fact even worse in that specifying CenterAndExpand binds nothing and you only see binding data when you rotate the device.

Sign In or Register to comment.