xamarin / Xamarin.Forms

Xamarin.Forms is no longer supported. Migrate your apps to .NET MAUI.
https://aka.ms/xamarin-upgrade
Other
5.63k stars 1.87k forks source link

CollectionView height doesn't adapt after height of its content #6554

Open luczha opened 5 years ago

luczha commented 5 years ago

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

adrianknight89 commented 5 years ago

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

LeoJHarris commented 5 years ago

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

karlingen commented 4 years ago

+1 on this

AnnaShaleva commented 4 years ago

+1

sebcsri commented 4 years ago

+1

kcrg commented 4 years ago

+2

angusbreno commented 4 years ago

+1

ioiooi commented 4 years ago

+1

Huaba93 commented 4 years ago

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.

ICDevelopments commented 4 years ago

+1

luczha commented 4 years ago

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

tiagotas commented 4 years ago

+1

shira164 commented 4 years ago

Hi All, Any update?

lomdar67 commented 4 years ago

+1

tscholze commented 4 years ago

+1

abhi-shukla commented 4 years ago

+1

eli191 commented 4 years ago

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

tscholze commented 4 years ago

@eli191 do you have any small sample? :)

eli191 commented 4 years ago

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

lorant-csonka-ICE commented 4 years ago

+1

Alextorres950325 commented 4 years ago

+1

mohsen-mohamed commented 4 years ago

+1

vanyok1991 commented 4 years ago

any update here? have similar issue

softlion commented 4 years ago

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;

Tommigun1980 commented 3 years ago

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.

softlion commented 3 years ago

@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 commented 3 years ago

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

neville-nazerane commented 3 years ago

Has anyone created a behavior for this yet?

sact1909 commented 3 years ago

Something new with this issue?

jerryhuang-net commented 3 years ago

In my case, this happens on CollectionView and ListView on iOS only (so far), and even on iOS, it only happens on physical device. So below is how I worked-around the issue:

private double width = 0; private double height = 0; protected override void OnSizeAllocated(double width, double height) { base.OnSizeAllocated(width, height); try { if (this.width != width || this.height != height) { this.width = width; this.height = height; //reconfigure layout if (this.height > 0 && Device.RuntimePlatform == Device.iOS) view.HeightRequest = this.Height - 50; }

        }
        catch (Exception ex)
        {
            vm.ErrorHandling(ex);
        }
    }

In this scenario, I know what is the height of the collection view should be, so deduct a value from the screen height does the trick. A little nasty, but works for me...

sact1909 commented 3 years ago

can you share a project in xamarin form on how to implement your solution @jerryhuang-net

vgnbk commented 3 years ago

Faced with the same issue and find this. In Workaround area there is a solution of this problem: Just replase <LinearItemsLayout>with <GridItemsLayout> inside CollectionView.ItemsLayout.

<CollectionView.ItemsLayout>
    <GridItemsLayout  
       HorizontalItemSpacing="10" 
       Orientation="Horizontal"
       SnapPointsType="MandatorySingle"
       SnapPointsAlignment="End"/>
</CollectionView.ItemsLayout>

After this change items inside CollectionView started to display correctly. Hope this will be helpfull.

kenchan97 commented 3 years ago

+1

3dfxuser commented 3 years ago

+1

Syed-Esqimo commented 3 years ago

Faced with the same issue and find this. In Workaround area there is a solution of this problem: Just replase <LinearItemsLayout>with <GridItemsLayout> inside CollectionView.ItemsLayout.

<CollectionView.ItemsLayout>
    <GridItemsLayout  
       HorizontalItemSpacing="10" 
       Orientation="Horizontal"
       SnapPointsType="MandatorySingle"
       SnapPointsAlignment="End"/>
</CollectionView.ItemsLayout>

After this change items inside CollectionView started to display correctly. Hope this will be helpfull.

I can confirm the above worked for me on a slighlty different context. In my case, I was setting Grid.RowHeight to 0 and back to original height and CollectionView with LinearLayout wasnt expanding back to its original height.

paul-charlton commented 2 years ago

I've had success with this (from https://stackoverflow.com/questions/68440238/how-to-set-auto-height-of-collection-view-in-xamarin-forms)

public class CollectionFitContentBehavior : Behavior<CollectionView>
{
    List<View> _itemsView;
    CollectionView _control;
    protected override void OnAttachedTo(CollectionView bindable)
    {
        base.OnAttachedTo(bindable);
        _control = bindable;
        _control.ChildAdded += ChildsAdded;
        _itemsView = new List<View>();
    }

    protected override void OnDetachingFrom(CollectionView bindable)
    {
        base.OnDetachingFrom(bindable);
        _control.ChildAdded -= ChildsAdded;

        foreach(var item in _itemsView)
            item.SizeChanged -= ChildSize;
    }

    private void ChildsAdded(object sender, ElementEventArgs e)
    {
        var cell = (e.Element as View);
        cell.SizeChanged += ChildSize;
        _itemsView.Add(cell);
    }

    private void ChildSize(object sender, EventArgs e)
    {
        var cell = (sender as View);
        _control.HeightRequest = _control.HeightRequest + cell.Height;
    }
}
AlasaliCS commented 1 year ago

I used VerticalOptions="FillAndExpand" for the entire "DataTemplate" and it is working for me.