Xamarin.Forms CollectionView: The Layout Flexibility Comes With a Composition Tax
Migrating from ListView to CollectionView in Xamarin.Forms buys flexible layouts and virtualization, but you must rebuild pull-to-refresh, grouping, and swipe actions yourself.
23 Oct 2025, 06:48 UTC

The stutter is not the control; it is the architecture
When a Xamarin.Forms list starts dropping frames on Android, the instinct is to blame the ListView. You swap it for CollectionView because the documentation calls it the modern replacement, and the scroll improves—until you notice that pull-to-refresh vanished, your grouping headers disappeared, and swipe-to-delete no longer works. The problem was never just frame rate. It is that CollectionView trades bundled convenience for architectural control, and that trade demands explicit decisions.
This article walks through the engineering trade-offs of moving list-heavy Xamarin.Forms UIs from ListView to CollectionView, where the flexibility pays off, and what you must rebuild before shipping.
Layout freedom without custom renderers
CollectionView arrived in the Xamarin.Forms 4.x line and matured through 5.x with a single promise: one control, many layouts. ListView was structurally opinionated. It expected a vertical stack of cells, and anything else—a horizontal carousel, a two-column grid—meant a custom renderer or a third-party library.
CollectionView removes the fixed cell type. You change arrangement with ItemsLayout, not platform code:
<CollectionView ItemsLayout="VerticalList" />
<CollectionView ItemsLayout="HorizontalList" />
<CollectionView ItemsLayout="GridItemLayout, Span=2" />
For media galleries or onboarding flows, snap points align items to a boundary without custom gestures. Empty states are handled by EmptyView, so a cleared ItemsSource renders a template instead of a blank screen. These are not minor conveniences; they eliminate renderer forks that fragment maintenance.
The composition tax: features you must now build
The cost of that flexibility is explicit composition. ListView shipped with behaviors that CollectionView expects you to assemble:
| Capability | ListView | CollectionView |
|---|---|---|
| Pull-to-refresh | Built-in properties | Wrap in RefreshView |
| Grouping headers | IsGroupingEnabled + display property | IsGrouped + GroupHeaderTemplate |
| Context actions | ContextActions collection | SwipeView or custom gestures |
Grouping is the sharpest edge. If you bind a flat ObservableCollection and set IsGrouped="True", the control will not throw. It will silently omit headers or render them incorrectly. You need a collection of groups, each exposing its own items.
A grouped catalog that actually renders
Suppose you are showing products by category. The data model must mirror the hierarchy:
public class ProductGroup : List<Product>
{
public string CategoryName { get; set; }
}
// In your view model:
public ObservableCollection<ProductGroup> GroupedProducts { get; set; }
The XAML binds to the outer collection and tells CollectionView to expect grouping:
<CollectionView ItemsSource="{Binding GroupedProducts}"
IsGrouped="True">
<CollectionView.GroupHeaderTemplate>
<DataTemplate>
<Label Text="{Binding CategoryName}"
FontAttributes="Bold"
Padding="8,4" />
</DataTemplate>
</CollectionView.GroupHeaderTemplate>
<CollectionView.ItemTemplate>
<DataTemplate>
<Label Text="{Binding Name}" />
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
If headers do not appear, verify that GroupedProducts is truly an ObservableCollection of group objects, not a flat list. A quick diagnostic: clear the source and confirm EmptyView appears; if it does, the control is alive and the data shape is the culprit.
Virtualization helps, but bindings still bite
CollectionView virtualizes items through its internal layout engine, reusing cells as they scroll off-screen. That is a structural improvement over ListView, but it is not a performance guarantee. Large lists still suffer when each row uses heavy DataTemplate triggers or classic reflection-based bindings.
The highest-leverage fix is compiled bindings. By setting x:DataType on a DataTemplate, the XAML compiler generates direct property access instead of runtime reflection:
<DataTemplate x:DataType="models:Product">
<Label Text="{Binding Name}" />
</DataTemplate>
Before optimizing templates, measure. Run the app on a physical mid-range Android device and capture a trace with Android Profiler or Xcode Instruments. If the main thread is busy in layout, simplify the visual tree. If it is busy in binding, add x:DataType. Guessing without a trace usually leads to premature abstraction.
Support reality: Xamarin.Forms is in maintenance
Here is the trade-off that outranks layout APIs: Xamarin.Forms has entered a maintenance phase. .NET MAUI is the evolution path, and Microsoft support policies and target framework requirements have shifted accordingly. Starting a new Xamarin.Forms project today means accepting a platform with a finite roadmap.
For legacy apps, the decision is rarely "rewrite everything." It is usually:
- Stay and stabilize: Keep Xamarin.Forms, but migrate
ListViewtoCollectionViewto reduce renderer debt and improve scroll behavior. - Bridge and prepare: Use .NET Standard libraries and MVVM patterns that translate cleanly to .NET MAUI, so a future migration is a UI-layer project, not a rewrite.
Verify current support policies and target framework requirements before committing engineering time to new Xamarin work. A conservative approach treats Xamarin.Forms as a stable runtime for existing features, not a greenfield target.
An actionable checklist
- Inventory every
ListViewin your app and note which features it uses: refresh, grouping, context actions. - Replace layouts first. Switch
ItemsLayoutto match your design; confirm on both iOS and Android simulators. - Rebuild missing features with
RefreshView,GroupHeaderTemplate, andSwipeView. Test grouping with a real groupedObservableCollection, not a flat list. - Add
x:DataTypecompiled bindings to your hottest templates. - Profile a 1,000-item list on physical hardware before and after the change.
- Document whether this app is a Xamarin.Forms end-state or a .NET MAUI migration candidate.
CollectionView is the right tool for list-heavy Xamarin.Forms UIs, but only if you treat its missing pieces as design requirements rather than bugs. Plan the composition, measure the scroll, and keep the migration path in view.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.