New to Telerik UI for Xamarin? Download free 30-day trial

Grouping

RadListView provides you with the functionality to programmatically group its data at runtime. This can be achieved through adding groupdescriptors to the RadListView.GroupDescriptors collection.

In addition, the control supports groups expand and collapse operations either through the UI by tapping on the group headers or programmatically. For more details on this refer to Expand and Collapse Groups.

PropertyGroupDescriptor

You can group the data by a property value from the class that defines your items. This descriptor exposes the following properties:

  • PropertyName: Defines the string name of the property you want to group by.
  • SortOrder: Defines the sort order in each group to Ascending or Descending.

Let's, for example, have the following business object:

public class City
{     
    public string Name { get; set; }
    public string Country { get; set; }
}

and a ViewModel with a collection of Cities:

public class ViewModel
{
    public ObservableCollection<City> Cities { get; set; }

    public ViewModel()
    {
        this.Cities = new ObservableCollection<City>()
        {
            new City() { Name = "Barcelona", Country = "Spain"},
            new City() { Name = "Madrid", Country = "Spain"},
            new City() { Name = "Rome", Country = "Italy"},
            new City() { Name = "Florence", Country = "Italy"},
            new City() { Name = "London", Country = "England"},
            new City() { Name = "Manchester", Country = "England"},
            new City() { Name = "New York", Country = "USA"},
            new City() { Name = "Boston", Country = "USA"}
         };
    }
}

Next snippet demonstrates how you could group the Cities by "Country" property through the PropertyGroupDescriptor:

<telerikDataControls:RadListView x:Name="listView" ItemsSource="{Binding Cities}"
                                 ItemTemplate="{StaticResource ListViewItemTemplate}"
                                 GroupHeaderTemplate="{StaticResource ListViewGroupHeaderTemplate}"
                                 GroupHeaderStyle="{StaticResource ListViewGroupHeaderStyle}">
    <telerikDataControls:RadListView.GroupDescriptors>
        <telerikListView:PropertyGroupDescriptor PropertyName="Country"/>
    </telerikDataControls:RadListView.GroupDescriptors>
</telerikDataControls:RadListView>

GroupHeaderTemplate

In addition, you could create custom GroupHeaderTemplate as well as ListViewItemTemplate in order to achieve the desired look when grouping the ListView. The BindingContext of the GroupHeader is a complex object and it includes the following properties:

  • IsExpanded: Defines a value indicating whether the group is currently expanded (has its child items visible).
  • Items: Gets the child items of the group.
  • Key: Gets the specific for the group key.
  • Level: Gets the zero-based level (or the depth) of the group.

The snippet below shows how the GroupHeaderTemplate is defined:

<ResourceDictionary>
    <DataTemplate x:Key="ListViewItemTemplate">
        <telerikListView:ListViewTemplateCell>
            <telerikListView:ListViewTemplateCell.View>
                <Grid Padding="16, 0, 0, 0" BackgroundColor="#F1F2F5">
                    <Label Text="{Binding Name}" TextColor="#6F6F70" FontSize="Small" />
                </Grid>
            </telerikListView:ListViewTemplateCell.View>
        </telerikListView:ListViewTemplateCell>
    </DataTemplate>
    <DataTemplate x:Key="ListViewGroupHeaderTemplate">
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <Label Text="&#x25B8;" Margin="8, 12, 0, 6" TextColor="DarkGray" FontSize="Medium">
                <Label.Triggers>
                    <DataTrigger TargetType="Label" Binding="{Binding IsExpanded}" Value="True">
                        <Setter Property="Text" Value="&#x25BE;" />
                    </DataTrigger>
                </Label.Triggers>
            </Label>
            <Label Margin="0, 12, 0, 6" Text="{Binding }" Grid.Column="1" TextColor="DarkGray" FontSize="Medium" HorizontalOptions="Start" />
        </Grid>
    </DataTemplate>
    <telerikListView:ListViewGroupStyle x:Key="ListViewGroupHeaderStyle" BackgroundColor="Transparent" />
</ResourceDictionary>

All that is left is to set the ViewModel as BindingContext of the control:

listView.BindingContext = new ViewModel();

Figure 1: ListView grouped through PropertyGroupDescriptor

ListView Grouping

DelegateGroupDescriptor

This descriptor enables you to group by a custom key (e.g. some complex expression combining two or more properties) instead of being limited by the value of a single property. This descriptor exposes the following properties:

  • KeyExtractor: Defines the (Func<object, object) delegate which returns the property to retrieve the group key for each data item.
  • SortOrder: Defines the sort order in each group to Ascending or Descending.

Let's use the same example from the previous section, just add DelegateGroupDescriptor through code instead.

Next snippet shows how the ListView instance is defined:

<telerikDataControls:RadListView x:Name="listView" ItemsSource="{Binding Cities}"
                                 ItemTemplate="{StaticResource ListViewItemTemplate}"
                                 GroupHeaderTemplate="{StaticResource ListViewGroupHeaderTemplate}"
                                 GroupHeaderStyle="{StaticResource ListViewGroupHeaderStyle}">
</telerikDataControls:RadListView>

And you could create and apply a delegate for grouping the items (for example by their first letter) as following:

public DelegateGroupDescriptorGroups()
{
    InitializeComponent();
    listView.BindingContext = new ViewModel();

    var delegateDescriptor = new DelegateGroupDescriptor
    {
        KeyExtractor = FirstLetterKeyExtractor
    };

    listView.GroupDescriptors.Add(delegateDescriptor);
}

private object FirstLetterKeyExtractor(object arg)
{
    var item = arg as City;
    return item?.Name.Substring(0, 1);
}

Figure 2: ListView grouped through DelegateGroupDescriptor

ListView Grouping

Sticky Group Headers

Starting with R1 2020 SP release RadListView provides the option to set its group headers as sticky. This means the GroupHeader will "freeze" while scrolling through the items until the whole group is scrolled away. As you scroll through the next group, the currently sticked group header will be pushed by the next group header.

To enable the sticky group headers behavior, just set IsGroupHeaderSticky property of the ListView to True. By default IsGroupHeaderSticky value is False.

<telerikDataControls:RadListView x:Name="listView" 
                                  IsGroupHeaderSticky="True"  />
var listView = new RadListView();
listView.IsGroupHeaderSticky = true;

Check below the sticky group headers in action:

ListView Sticky Group Headers

Bindable GroupDescriptor

The GroupDescriptor collection now can be controlled by users using MVVM.

In order to control the GroupDescriptor collection through MVVM:

  1. Create a property of type ObservableCollection in your ViewModel which will contain the needed group descriptors:

    public ObservableCollection<GroupDescriptorBase> GroupDescriptors
    {
    get
    {
        return this.groupDescriptors;
    }
    set
    {
        if (this.groupDescriptors != value)
        {
            this.groupDescriptors = value;
            OnPropertyChanged();
        }
    }
    }
    
  2. Use OneWayToSource binding mode to bind that property to the GroupDescriptors property of RadListView:
<telerikDataControls:RadListView x:Name="listView" 
                                 Grid.Row="2" 
                                 GroupDescriptors="{Binding GroupDescriptors, 
                                 Mode=OneWayToSource}"  
                                 ItemsSource="{Binding Items}">

Here is how the GroupDescriptor collection looks like through MVVM:

GroupDescriptorsMVVM

You can find a working demo labeled Bindable Group Descriptors in the ListView/Bindable Collections folder of the SDK Samples Browser application.

See Also

In this article