DEV Community

Cover image for Deprecation of ListView in .NET MAUI
Victor Hugo Garcia
Victor Hugo Garcia

Posted on

8

Deprecation of ListView in .NET MAUI

Just a few hours ago, the .NET MAUI team announced a significant change coming in .NET 10: the ListView control and all its related cell types (TextCell, ImageCell, ViewCell, etc.) will be marked as obsolete. This decision is part of Microsoft's strategy to streamline the developer experience by focusing on a single, optimized control for displaying collections of data. Which I'm personally happy with, the less duplicated controls to maintain the better.

If you've been using MAUI (or Xamarin.Forms before it), you're likely familiar with ListView - it's been a staple for displaying lists of data since the early days. However, CollectionView in the past had several performance and rendering issues, but these days thanks to Microsoft and the community, the Collection View offers numerous advantages, including better performance, more flexible layouts, and improved customization options. With .NET 10 on the horizon, now is the perfect time to migrate your existing ListView implementations to CollectionView.

In this article, I'll walk you through a straightforward migration process, highlighting the key differences between these controls and providing practical examples to ensure a smooth transition for your MAUI applications.

Understanding the Key Differences

Before diving into the migration process, it's helpful to understand some fundamental differences between ListView and CollectionView:

Feature ListView CollectionView
Cell Types Uses predefined cells (TextCell, ImageCell, etc.) Uses DataTemplates directly
Selection Single or multiple selection with built-in visual feedback Single or multiple but even more flexible selection with customizable visual feedback
Item Appearance Uses Cell hierarchy Uses direct DataTemplate
Layouts Vertical list only Vertical, horizontal, and grid layouts
Performance Less optimized Better virtualization and performance
Grouping Through GroupDisplayBinding More flexible grouping options
Headers/Footers Basic header/footer templates Enhanced header/footer templates

Step 1: Replace the Control Declaration

The first step is to replace the ListView declaration with CollectionView in your XAML:

Before (ListView):

<ListView x:Name="MyListView" 
          ItemsSource="{Binding Items}">
    <!-- Cell templates here -->
</ListView>
Enter fullscreen mode Exit fullscreen mode

After (CollectionView):

<CollectionView x:Name="MyCollectionView"
                ItemsSource="{Binding Items}">
    <!-- Item templates here -->
</CollectionView>
Enter fullscreen mode Exit fullscreen mode

Step 2: Convert Cell Templates to DataTemplates

One of the biggest differences is how items are templated. ListView uses various cell types, while CollectionView uses DataTemplate directly.

Example: Converting TextCell

Before (ListView with TextCell):

<ListView ItemsSource="{Binding Items}">
    <ListView.ItemTemplate>
        <DataTemplate>
            <TextCell Text="{Binding Title}" 
                      Detail="{Binding Description}" />
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>
Enter fullscreen mode Exit fullscreen mode

After (CollectionView):

<CollectionView ItemsSource="{Binding Items}">
    <CollectionView.ItemTemplate>
        <DataTemplate>
            <Grid Padding="10">
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                </Grid.RowDefinitions>
                <Label Grid.Row="0" Text="{Binding Title}" FontAttributes="Bold" />
                <Label Grid.Row="1" Text="{Binding Description}" FontSize="Small" />
            </Grid>
        </DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView>
Enter fullscreen mode Exit fullscreen mode

Example: Converting ImageCell

Before (ListView with ImageCell):

<ListView ItemsSource="{Binding Items}">
    <ListView.ItemTemplate>
        <DataTemplate>
            <ImageCell Text="{Binding Title}" 
                       Detail="{Binding Description}"
                       ImageSource="{Binding ImageUrl}" />
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>
Enter fullscreen mode Exit fullscreen mode

After (CollectionView):

<CollectionView ItemsSource="{Binding Items}">
    <CollectionView.ItemTemplate>
        <DataTemplate>
            <Grid Padding="10" ColumnSpacing="10">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="50" />
                    <ColumnDefinition Width="*" />
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                </Grid.RowDefinitions>

                <Image Grid.Row="0" Grid.RowSpan="2" Grid.Column="0"
                       Source="{Binding ImageUrl}"
                       Aspect="AspectFill"
                       HeightRequest="50" WidthRequest="50" />

                <Label Grid.Row="0" Grid.Column="1" 
                       Text="{Binding Title}" 
                       FontAttributes="Bold" />

                <Label Grid.Row="1" Grid.Column="1" 
                       Text="{Binding Description}" 
                       FontSize="Small" />
            </Grid>
        </DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView>
Enter fullscreen mode Exit fullscreen mode

Step 3: Update Selection Handling

The selection mechanism differs between the two controls:

Before (ListView):

void OnItemSelected(object sender, SelectedItemChangedEventArgs e)
{
    if (e.SelectedItem == null)
        return;

    // Handle the selected item
    var selectedItem = e.SelectedItem as MyItemType;

    // Important: Deselect the item
    ((ListView)sender).SelectedItem = null;
}
Enter fullscreen mode Exit fullscreen mode

After (CollectionView):

<CollectionView
      x:Name="collectionView"
      SelectionMode="Single"
      SelectionChanged="OnSelectionChanged">
</CollectionView>
Enter fullscreen mode Exit fullscreen mode
async void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
    var previous = e.PreviousSelection.FirstOrDefault();
    var current = e.CurrentSelection.FirstOrDefault();
}
Enter fullscreen mode Exit fullscreen mode

Step 4: Convert Grouping

If you're using grouping in your ListView, you'll need to adapt it for CollectionView:

Before (ListView with grouping):

<ListView ItemsSource="{Binding GroupedItems}" 
          IsGroupingEnabled="True"
          GroupDisplayBinding="{Binding Key}">
    <ListView.ItemTemplate>
        <!-- Item template -->
    </ListView.ItemTemplate>
</ListView>
Enter fullscreen mode Exit fullscreen mode

After (CollectionView with grouping):

<CollectionView ItemsSource="{Binding GroupedItems}" 
                IsGrouped="True">
    <CollectionView.GroupHeaderTemplate>
        <DataTemplate>
            <Label Text="{Binding Key}" 
                   FontAttributes="Bold"
                   BackgroundColor="LightGray"
                   Padding="10" />
        </DataTemplate>
    </CollectionView.GroupHeaderTemplate>
    <CollectionView.ItemTemplate>
        <!-- Item template -->
    </CollectionView.ItemTemplate>
</CollectionView>
Enter fullscreen mode Exit fullscreen mode

Step 5: Headers and Footers

Converting headers and footers is straightforward:

Before (ListView):

<ListView ItemsSource="{Binding Items}">
    <ListView.Header>
        <Label Text="Items List" FontSize="Large" />
    </ListView.Header>
    <ListView.Footer>
        <Label Text="End of list" />
    </ListView.Footer>
    <!-- Item template -->
</ListView>
Enter fullscreen mode Exit fullscreen mode

After (CollectionView):

<CollectionView ItemsSource="{Binding Items}">
    <CollectionView.Header>
        <Label Text="Items List" FontSize="Large" />
    </CollectionView.Header>
    <CollectionView.Footer>
        <Label Text="End of list" />
    </CollectionView.Footer>
    <!-- Item template -->
</CollectionView>
Enter fullscreen mode Exit fullscreen mode

Step 6: Take Advantage of New Layout Options

One significant advantage of CollectionView is its flexible layout options:

Vertical List (default):

<CollectionView ItemsSource="{Binding Items}">
    <CollectionView.ItemsLayout>
        <LinearItemsLayout Orientation="Vertical" ItemSpacing="5" />
    </CollectionView.ItemsLayout>
    <!-- Item template -->
</CollectionView>
Enter fullscreen mode Exit fullscreen mode

Horizontal List:

<CollectionView ItemsSource="{Binding Items}">
    <CollectionView.ItemsLayout>
        <LinearItemsLayout Orientation="Horizontal" ItemSpacing="5" />
    </CollectionView.ItemsLayout>
    <!-- Item template -->
</CollectionView>
Enter fullscreen mode Exit fullscreen mode

Grid Layout:

<CollectionView ItemsSource="{Binding Items}">
    <CollectionView.ItemsLayout>
        <GridItemsLayout Orientation="Vertical" 
                         Span="2"
                         HorizontalItemSpacing="5"
                         VerticalItemSpacing="5" />
    </CollectionView.ItemsLayout>
    <!-- Item template -->
</CollectionView>
Enter fullscreen mode Exit fullscreen mode

Step 7: Handle Empty State

CollectionView has better support for empty state handling:

<CollectionView ItemsSource="{Binding Items}">
    <CollectionView.EmptyView>
        <StackLayout Padding="20">
            <Label Text="No items available" 
                   HorizontalOptions="Center"
                   VerticalOptions="Center" />
            <Button Text="Refresh" Command="{Binding RefreshCommand}" />
        </StackLayout>
    </CollectionView.EmptyView>
    <!-- Item template -->
</CollectionView>
Enter fullscreen mode Exit fullscreen mode

Common Challenges and Solutions

Challenge 1: Context Actions

ListView's ContextActions don't have a direct equivalent in CollectionView. Instead, you can use SwipeView:

Before (ListView with ContextActions):

<ListView ItemsSource="{Binding Items}">
    <ListView.ItemTemplate>
        <DataTemplate>
            <ViewCell>
                <ViewCell.ContextActions>
                    <MenuItem Text="Edit" Command="{Binding Source={RelativeSource AncestorType={x:Type vm:MyViewModel}}, Path=EditCommand}" CommandParameter="{Binding .}" />
                    <MenuItem Text="Delete" Command="{Binding Source={RelativeSource AncestorType={x:Type vm:MyViewModel}}, Path=DeleteCommand}" CommandParameter="{Binding .}" />
                </ViewCell.ContextActions>
                <Label Text="{Binding Title}" />
            </ViewCell>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>
Enter fullscreen mode Exit fullscreen mode

After (CollectionView with SwipeView):

<CollectionView ItemsSource="{Binding Items}">
    <CollectionView.ItemTemplate>
        <DataTemplate>
            <SwipeView>
                <SwipeView.RightItems>
                    <SwipeItems>
                        <SwipeItem Text="Edit" 
                                   Command="{Binding Source={RelativeSource AncestorType={x:Type vm:MyViewModel}}, Path=EditCommand}" 
                                   CommandParameter="{Binding .}"
                                   BackgroundColor="Blue" />
                        <SwipeItem Text="Delete" 
                                   Command="{Binding Source={RelativeSource AncestorType={x:Type vm:MyViewModel}}, Path=DeleteCommand}" 
                                   CommandParameter="{Binding .}"
                                   BackgroundColor="Red" />
                    </SwipeItems>
                </SwipeView.RightItems>
                <Grid Padding="10">
                    <Label Text="{Binding Title}" />
                </Grid>
            </SwipeView>
        </DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView>
Enter fullscreen mode Exit fullscreen mode

Challenge 2: HasUnevenRows

ListView had HasUnevenRows for variable height rows. With CollectionView, rows are automatically sized based on content:

Before (ListView):

<ListView ItemsSource="{Binding Items}" HasUnevenRows="True">
    <!-- Item template -->
</ListView>
Enter fullscreen mode Exit fullscreen mode

After (CollectionView):

<CollectionView ItemsSource="{Binding Items}">
    <!-- No equivalent property needed - it's the default behavior -->
    <!-- Item template -->
</CollectionView>
Enter fullscreen mode Exit fullscreen mode

Conclusion

With .NET 10 marking ListView as obsolete, now is the ideal time to migrate your MAUI applications to use CollectionView. The transition may require some initial effort, especially if you have complex templates or custom behaviors, but the benefits are substantial. CollectionView offers better performance, more flexible layouts, and an overall improved developer experience.

The migration process outlined in this article provides a straightforward path to update your applications, covering the most common scenarios you'll encounter. By embracing CollectionView now, you'll future-proof your applications and take advantage of the enhancements that Microsoft continues to make to this control.

Remember that while the obsolescence marking begins with .NET 10 Preview 3, ListView will continue to function in .NET 10, giving you some time to complete your migration. However, bug fixes for ListView will generally not be prioritized, so it's best to start the transition sooner rather than later.

Have you encountered any specific challenges in migrating from ListView to CollectionView? Let me know in the comments below, and let's work through them together!

Top comments (0)