CollectionView inside ScrollView

Table of Contents

Learn how to use the CollectionView within a ScrollView in Xamarin.Forms. Sometimes we want to develop some functionalities that need different data contexts to be displayed within the same page and this can be achieved with a TemplateSelector if the data source is the same, but if it is not the same how we can do it?.

In this article, I will share with you custom control of the CollectionView that you can use directly in your projects or simply adapt it to your needs if that’s what you want.

CollectionView inside a ScrollView

Before showing you the control and how it works I need you to understand that it is not always highly recommended to put a CollectionView inside a ScrollView simply because both controls have scrolling and when using them together you can have unwanted User Experiences (UX).

Here’s an example of how a CollectionView behaves within a ScrollView:

CollectionView within a ScrollVIew.

Note: The displayed header does not belong to the CollectionView.

If you look good, until the CollectionView reaches the bottom of the list of items it handles, the ScrollView does not scroll unless you scroll from the header which does not belong to the CollectionView.

Why use a CollectionView within a ScrollView?

I’ll show you a particular and very popular case for you to understand. This is the Instagram user page. Let’s go see it:

@luismatosluna

If you look, below the stories, are the TabsViews that allow us to visualize different types of data and contexts.

 NotePreviously, Instagram displayed in tabsView the type of view you wanted to see on the page either Grid or List. This behavior in Xamarin.Forms can be achieved with a TemplateSelector as long as the data context is the same.

Here the challenge is that the CollectionView is not very extensible to say, and does not allow you to achieve this functionality in a natural way, at least in what you can investigate.

That’s why I decided to create the next control.

Custom CollectionView

Control really is a very simple thing but it applies some powerful concepts. What we will do will be disable the scroll of the in order to use only the scroll of the . For this we only have to calculate the total height of all items and set that height to .CollectionView ScrollViewCollectionView

To achieve this we have defined some default parameters as:

  • ScrollView, of type ScroolView, receives the instance of the ScrollView parent and has a default value of null.
  • RowHeigt, of type int, receives the predefined height of the rows that it is using the control, default 0.


With these parameters we can have one CustomCollactionView as follows:

    public class CustomCollectionView : CollectionView
    {
...
        [TypeConverter(typeof(ReferenceTypeConverter))]
        public ScrollView ScrollView
        {
            set
            {
                _scrollView = value;
                _scrollView.Scrolled += _scrollView_Scrolled;
            }
        }
...
        private void _scrollView_Scrolled(object sender, ScrolledEventArgs e)
        {
            double scrollingSpace = _scrollView.ContentSize.Height - _scrollView.Height;
            if (scrollingSpace <= e.ScrollY)
            {
                // Touched bottom view
                RemainingItemsThresholdReachedCommand?.Execute(RemainingItemsThresholdReachedCommandParameter);
            }
        }

        private void UpdateHeight()
        {
            if(_columns  == 0)
            {
                if (ItemsLayout is GridItemsLayout layout)                
                    _columns = layout.Span;
                else
                    _columns = 1;
            }

            if (_rowHeigt > 0)            
                HeightRequest = (_rowHeigt * _rowCount) / _columns;
        }

        protected override void OnChildAdded(Element child)
        {
            base.OnChildAdded(child);
            UpdateHeight();
        }
        
        protected override void OnChildRemoved(Element child, int oldLogicalIndex)
        {
            base.OnChildRemoved(child, oldLogicalIndex);
            UpdateHeight();
        }
    }

Each time it is added or deleted, an item will update the height of the CollectionView. Similarly, if we touch the bottom of the ScrollView we will execute the RemainingItemsThresholdReachedCommand of the CollectinView to make sure that the behavior is the same in every way.

Once we have control, we can use it in our Xamarin Forms projects. Here’s an example in XAML:

...
    <ScrollView x:Name="myScrollView">
        <StackLayout>

            <!-- YOURS CONTROLS GOES HERE -->

            <ctrls:CustomCollectionView
                ItemSizingStrategy="MeasureFirstItem"
                ItemsLayout="VerticalGrid, 3"
                ItemsSource="{Binding Items}"
                RowHeigt="120"
                ScrollView="myScrollView">

                <CollectionView.ItemTemplate>
                    <DataTemplate>
                        ...
                    </DataTemplate>
                </CollectionView.ItemTemplate>
            </ctrls:CustomCollectionView>

        </StackLayout>
    </ScrollView>
...

With this, we can get behavior similar to that of the Instagram profile page. Let’s look at an example:

CollectionView within a ScrollVIew.

Note: The displayed header does not belong to the CollectionView.

Conclusion

As you can see this is very simple but powerful control, which I bet more than one can help. The implementation is quite simple and very simple to understand.

NoteThere is a limitation. The control does not work with rows that have dynamic heights, because we are calculating predefined fields.

I hope this article is useful, and nothing let me know on the networks that they really like these kinds of articles and I will do my best to give them the best of the best.

Share this content!

Facebook
Twitter
LinkedIn
Telegram
WhatsApp