5 Quick Tips For CollectionView

David Ortinau

Quick Tips to get started with CollectionView! Below are some useful new features and best practices for creating powerful native mobile experiences.

A Few Quick Tips

 

Loading Infinitely More

A few new properties are introduced on CollectionView that make it really simple to fetch more data as the user is scrolling. In this example, the mock data has a thousand items however I only want to initially load 20 results. If the user continues scrolling to the end, CollectionView will execute RemainingItemsThresholdReachedCommand to fetch additional data. This continues on and on until the user has fetched all the data available, or if you are a massive social network then perhaps “never”.

Quick Tip One

<CollectionView
  x:Name="ResultsList"
  ItemsSource="{Binding FlightsToDisplay}"
  ItemSizingStrategy="MeasureFirstItem"
  RemainingItemsThreshold="0"
  RemainingItemsThresholdReachedCommand="{Binding LoadMoreFlightsCommand}">

If you wish to start fetching additional data sometime before the user reaches the end of the collection, you can set the RemainingItemsThreshold to any positive number. When that number is reached, the command will be called.

Make it Snap

Sometimes you want to have items snap into position rather than scrolling freely. CollectionView provide a few properties to enable this behavior on any layout. Consider the example below:

<CollectionView>
    <CollectionView.ItemsLayout>
        <LinearItemsLayout Orientation="Horizontal"    
                           SnapPointsType="MandatorySingle"
                           SnapPointsAlignment="Start"
                           ItemSpacing="10"/>
    </CollectionView.ItemsLayout>

Quick Tip Two
By setting the snap point to “MadatorySingle” and the alignment to “Start”, the item nearest the starting edge will snap into the first position so it’s entirely visible.

Keep Them Separated

CollectionView avoids a few legacy concepts from ListView that added confusion and bogged down performance including ViewCell and item separators. Adding those lines between items in your list can be very convenient when you want them, but it’s not a concept included in the native controls.

Quick Tip Three
So how do you get those lines between elements? It’s really as easy as adding an element at the bottom of your item template. Here’s one example using a BoxView to create a horizontal line that spans the width of the template and provides a vertical margin for spacing.

<CollectionView.ItemTemplate>
  <DataTemplate>
      <Grid>
          <Grid.ColumnDefinitions>
              <ColumnDefinition Width="1*"/>
              <ColumnDefinition Width="3*"/>
          </Grid.ColumnDefinitions>

          <Grid.RowDefinitions>
              <RowDefinition Height="4"/>
              <RowDefinition Height="*"/>
              <RowDefinition Height="1"/>
          </Grid.RowDefinitions>

          <Image Grid.RowSpan="3" VerticalOptions="Center">
              <Image.Source>
                  <FontImageSource
                  FontFamily="{StaticResource FontAwesome}"
                  Glyph="{Binding Type}"
                  Color="{StaticResource PrimaryColor}"
                  Size="24"
                  />
              </Image.Source>
          </Image>

          <StackLayout
              Grid.Row="1" 
                 Grid.Column="1">
          <Label  
                 Text="{Binding Title}" 
                 LineBreakMode="TailTruncation"/>
          <Label Text="{Binding Message}"
                 LineBreakMode="WordWrap"
                 MaxLines="2"/>
          <Label Text="{Binding PostedAgo}"/>
          </StackLayout>

          <!-- Separator -->
          <BoxView HeightRequest="1"
                   BackgroundColor="Black"
                   Grid.ColumnSpan="2"
                   Grid.Row="2"
                   VerticalOptions="End"/>
      </Grid>
  </DataTemplate>
</CollectionView.ItemTemplate>

Handling Item Taps

When you look at add gestures to your CollectionView you may be tempted to use the SelectionChanged event or SelectionChangedCommand on the control itself. You absolutely can make that work for handling a tap and navigating to another page, for example. This isn’t however the best use of those for this very common purpose. Those properties are for selecting one or more items in the collection to then take a further action on them such as deletion.

As much more direct approach is to add a TapGestureRecognizer to your ItemTemplate.

<CollectionView.ItemTemplate>
    <DataTemplate>
        <views:ResultViewA>
            <views:ResultViewA.GestureRecognizers>
                <TapGestureRecognizer
                    Command="{Binding 
                        Source={RelativeSource 
                        AncestorType={x:Type vm:FlightResultsViewModel}}, 
                            Path=GoToDetailsCommand}"
                    CommandParameter="{Binding .}"/>
            </views:ResultViewA.GestureRecognizers>
        </views:ResultViewA>
    </DataTemplate>
</CollectionView.ItemTemplate>

Keep in mind that when working in the ItemTemplate the BindingContext is the item itself and not that of the ContentPage. If your command exists on the item, then you’re all set. However if you wish to route this binding to a command on your ContentPage‘s view model as in this example you can use the newly supported RelativeSource.

Quick Tip Four
Further, by setting the CommandParameter to the current binding context of the item, you’ll easily get the data item you are working with passed to the command.

Sizing for Performance

If you know all your items will be of the same size, you’ll get a performance benefit by setting the item sizing strategy up front to MeasureFirstItem. This way CollectionView will measure the first item alone, and cache that sizing for all items.

<CollectionView
  x:Name="ResultsList"
  ItemsSource="{Binding FlightsToDisplay}"
  ItemSizingStrategy="MeasureFirstItem"

This may not always be possible, or desirable. Perhaps you have very little control over the content you need to display because it is user generated. By default CollectionView uses MeasureAllItems to measure each item and size them according to their content needs. In ListView this would be equivalent to setting the HasUnevenRows property to true.

 

Get More Quick Tips

CollectionView has much more to offer such as adding pull-to-refresh with the new Refresh View and additional layout options for horizontal scrolling, grids, and custom layouts. Check out these useful topics from our documentation and samples:

All code and screenshots above are from the FlyMe demo app that I created for Microsoft Ignite. Slides and video of the session are available here.

2 comments

Discussion is closed. Login to edit/delete existing comments.

  • Periyasamy Shanmugam 0

    Very useful tips, we expect more from you. thanks

  • محمد حسین فخرآوری 0

    hi
    Java.Lang.NullPointerException: ‘Attempt to invoke virtual method ‘void android.graphics.Rect.set(android.graphics.Rect)’ on a null object reference’

    CollectionView Bored of this error
    I’m displaying a simple list, but this error occurs while scrolling

    plase show post
    https://forums.xamarin.com/discussion/comment/406079#Comment_406079

Feedback usabilla icon