locked
Can't get ListView to vertically center within StackLayout RRS feed

  • Question

  • User245594 posted

    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?

    Thursday, July 28, 2016 6:41 PM

All replies

  • User89714 posted

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

    Thursday, July 28, 2016 7:56 PM
  • User245594 posted

    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.

    Thursday, July 28, 2016 9:31 PM
  • User89714 posted

    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.

    Thursday, July 28, 2016 9:58 PM
  • User2148 posted

    @JohnHardman Xamarin people never sleep ;)

    Thursday, July 28, 2016 10:52 PM
  • User226261 posted

    @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

    Friday, July 29, 2016 12:09 AM
  • User245594 posted

    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?

    Friday, July 29, 2016 1:52 AM
  • User52634 posted

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

    Friday, July 29, 2016 7:55 AM
  • User89714 posted

    @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.

    Friday, July 29, 2016 10:06 AM
  • User222624 posted

    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.

    Friday, July 29, 2016 2:14 PM
  • User245594 posted

    @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.

    Monday, August 1, 2016 3:20 AM
  • User89714 posted

    @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 :-)

    Monday, August 1, 2016 7:44 AM
  • User245594 posted

    @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.

    Monday, August 1, 2016 4:50 PM
  • User31385 posted

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

    Monday, August 1, 2016 11:00 PM
  • User89714 posted

    @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 :-)

    Monday, August 1, 2016 11:11 PM
  • User31385 posted

    @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;
        }
    }
    

    }

    ```

    Tuesday, August 2, 2016 12:32 AM
  • User89714 posted

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

    Tuesday, August 2, 2016 8:25 AM
  • User176749 posted

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

    I thought it was only me :hushed:

    Saturday, August 12, 2017 10:30 PM
  • User76049 posted

    @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.

    Tuesday, August 22, 2017 11:23 AM