Xamarin.forms: CollectionView height doesn't adapt after height of its content

Created on 17 Jun 2019  Â·  29Comments  Â·  Source: xamarin/Xamarin.Forms

Summary

I was hoping for CollectionView to adapt its height after it's content (When ItemsLayout is set as vertical as it is in default that is). Which just like UICollectionView in native iOS .
Capture

collectionview layout high impact proposal-open enhancement âž•

Most helpful comment

sampleCollectionView.zip

Below in yellow is a Collectionview with a DataTemplate with a Frame and a nested label, I dont see why this should be stretching that much space vertically. The orientation of the CollectionView is set to horizontal. The way I see it only need the CollectionView to fit the frame and nested label. If this is default behavour then it would be better if the CollectionView would simply fit the content without having to set the HeightRequest (which is my work around).

device-2019-06-27-090345

All 29 comments

@luczha Can you attach a repro? Also, can you test your code with the latest pre-release?

Same issue here, I switched some views that were nested in a stacklayout that was nested in a scrollview and the height of the content were behaving like a fill, when a I swapped to use a collection view with horizontal orientation the CollectionView's Height was then filling and expanding the entire screen almost. I am forced to set the HeightRequest which isnt ideal as the CollectionView Height is ballooning.

sampleCollectionView.zip

Below in yellow is a Collectionview with a DataTemplate with a Frame and a nested label, I dont see why this should be stretching that much space vertically. The orientation of the CollectionView is set to horizontal. The way I see it only need the CollectionView to fit the frame and nested label. If this is default behavour then it would be better if the CollectionView would simply fit the content without having to set the HeightRequest (which is my work around).

device-2019-06-27-090345

+1 on this

+1

+1

+2

+1

+1

Same problem here. it doesn't even show all entries.

Tried it with BindableLayout and it worked as expected:

<ScrollView Orientation="Horizontal">
    <StackLayout Orientation="Horizontal" Spacing="20" Padding="0,20,0,20"
                 BindableLayout.ItemsSource="{Binding LeaderboardUsers}"
                 BindableLayout.ItemTemplateSelector="{StaticResource RankedUserDataTemplateSelector}" />
</ScrollView>

CollectionView does not work (vertically all items are displayed and height is also ok):

<CollectionView x:Name="LeaderboardCollectionView" ItemsSource="{Binding LeaderboardUsers}"
                ItemTemplate="{StaticResource RankedUserDataTemplateSelector}"
                VerticalOptions="Start" ItemSizingStrategy="MeasureAllItems">
    <CollectionView.ItemsLayout>
        <LinearItemsLayout Orientation="Horizontal" ItemSpacing="20" />
    </CollectionView.ItemsLayout>
</CollectionView>

collectionView

If you need a sample repo let me know.

+1

@samhouts @adrianknight89 Hi ,all .Any update ? Or it would better if there is a workaround . We can implement it by using custom renderer in Android . However , in iOS there is no such a property or API .

+1

Hi All, Any update?

+1

+1

+1

A simple solution is to calculate the height and set it with a MVVM auto-updating parameter.

@eli191 do you have any small sample? :)

@tscholze I am doing it through ReactiveUI so it won't help, but basically you will add
HeightRequest="{Binding CustomColViewHeight}" in the xaml
And in your ViewModel, have that property CustomColViewHeight implementing INPC.

+1

+1

+1

any update here? have similar issue

This is not an issue. This is by design.
A CollectionView is a ScrollView.
And a ScrollView does not autosize depending of its content. Instead it scrolls its content when this content is larger than the scroll's viewport (width or height).

So if you still want to autoheight a ScrollView, sum the height of each element and each separator, and set the HeightRequest of the CollectionView to this value. At runtime.
Maybe someone have created a behavior for this already: yourcollection.HeightRequest = yourcollection.ContentSize.Height;

This is not an issue. This is by design.
A CollectionView is a ScrollView.
And a ScrollView does not autosize depending of its content. Instead it scrolls its content when this content is larger than the scroll's viewport (width or height).

So if you still want to autoheight a ScrollView, sum the height of each element and each separator, and set the HeightRequest of the CollectionView to this value. At runtime.
Maybe someone have created a behavior for this already: yourcollection.HeightRequest = yourcollection.ContentSize.Height;

There really should be a property that can be turned on for when this behaviour is desired.
One use case is for example when you have multiple expanders on top of each other showing different collection view data.
I resorted to converting these back to bindable layouts for now.

It gets really unwieldy setting the height manually with collections that can change as you will have to add all kinds of listeners in code for something that should imho be built into the component.

@Tommigun1980 both iOS and Android uses optimized virtual lists which includes fast scrolling with acceleration. To be able to do that, they don't compute the height of each item as this would require a full layout of each item view, plus a binding of each item view with its datamodel (as you know, those virtual lists keep only about 10 views - they don't create a view for each item).

Instead they suppose each item has a certain height (which you can give). This height is mainly used for displaying the scroll handle on the right while scrolling, and detect the end of the list is to stop scrolling smoothly.

As Android does support dynamic height change of visible items, iOS does not. On iOS the correct way to do this is to remove/add the item again in a "transaction" so it can smoothly animate the change. But transactions are not available on out-of-the-box ObservableCollections on .NET ... even on .NET 5. And of course is not implemented in the backed Items container on iOS in Xamarin Forms.

Well that i not exactly true. Xamarin Forms iOS does implement a fake transaction by buffering changes happening on the same thread. When the main thread is free, it applies the changes.

@Tommigun1980 both iOS and Android uses optimized virtual lists which includes fast scrolling with acceleration. To be able to do that, they don't compute the height of each item as this would require a full layout of each item view, plus a binding of each item view with its datamodel (as you know, those virtual lists keep only about 10 views - they don't create a view for each item).

Instead they suppose each item has a certain height (which you can give). This height is mainly used for displaying the scroll handle on the right while scrolling, and detect the end of the list is to stop scrolling smoothly.

As Android does support dynamic height change of visible items, iOS does not. On iOS the correct way to do this is to remove/add the item again in a "transaction" so it can smoothly animate the change. But transactions are not available on out-of-the-box ObservableCollections on .NET ... even on .NET 5. And of course is not implemented in the backed Items container on iOS in Xamarin Forms.

Well that i not exactly true. Xamarin Forms iOS does implement a fake transaction by buffering changes happening on the same thread. When the main thread is free, it applies the changes.

When the measurement strategy is MeasureFirstItem I don't see why it couldn't be done, as it'd be analogous to a BindableLayout (with virtualisation support). It'd just be 'items * firstItemHeight'. It's such a common use case that I'd love it if it was supported.

PS. I deleted my previous reply to you as I thought I was in a different bug report so my context was incorrect.

Has anyone created a behavior for this yet?

Was this page helpful?
0 / 5 - 0 ratings