VirtualListView for .NET MAUI

repository·main·Indexed 42 days ago

https://github.com/redth/maui.virtuallistview

A high-performance, virtualized list control for .NET MAUI designed for large, multi-templated, and unevenly sized lists. It utilizes an adapter pattern via the IVirtualListViewAdapter interface and provides built-in implementations like VirtualListViewAdapter and ObservableCollectionAdapter. Features include DataTemplateSelectors, VirtualViewCell for position-aware styling, pull-to-refresh, and support for single or multiple selection modes.

Tokens
2.1K
Snippets
6
Records
9
Agent score
4%

What's inside VirtualListView

  1. Create a custom adapter by subclassing VirtualListViewAdapterBase

    main

    For complex data sources like SQLite, subclass VirtualListViewAdapterBase<TSection, TItem>. This allows you to implement custom logic for retrieving sections and items while managing your own caching strategy. Always call base.InvalidateData() and clear your local caches inside your InvalidateData() override.

    public class SQLiteAdapter : VirtualListViewAdapterBase<object, ItemInfo>
    {
    	public SQLiteAdapter() : base() { Db = new Database(...); }
    	public Database Db { get; }
    	int? cachedItemCount = null;
    
    	public override int GetNumberOfItemsInSection(int sectionIndex)
    		=> cachedItemCount ??= Db.ExecuteScalar<int>("SELECT COUNT(Id) FROM Items");
    
    	public override string GetItem(int sectionIndex, int itemIndex)
    		=> Db.FindWithQuery<ItemInfo>("SELECT * FROM Items ORDER BY Id LIMIT 1 OFFSET ?", itemIndex);
    
    	public override void InvalidateData()
    	{
    		cachedItemCount = null;
    		base.InvalidateData();
    	}
    }
  2. Use VirtualViewCell for advanced template styling

    main

    Wrap your template content in a VirtualViewCell to access position-aware properties. These properties allow you to easily implement features like separators, selection highlights, or specific styling for first/last items in a section.

    Properties available on VirtualViewCell (and via IPositionInfo):

    • SectionIndex, ItemIndex
    • IsGlobalHeader, IsGlobalFooter, IsSectionHeader, IsSectionFooter, IsItem
    • IsLastItemInSection, IsFirstItemInSection, IsNotLastItemInSection, IsNotFirstItemInSection
    • IsSelected

    You can access these via Binding or as DynamicResource values automatically populated by the cell.

    <xct:VirtualViewCell
        xmlns:xct="clr-namespace:Microsoft.Maui.Controls;assembly=VirtualListView"
        x:Class="VirtualListViewSample.GenericViewCell">
        <xct:VirtualViewCell>
            <VerticalStackLayout
                BackgroundColor="{Binding Source={x:Reference self}, Path=IsSelected, Converter={StaticResource selectedColorConverter}}">
    
                <BoxView
                    IsVisible="{DynamicResource IsNotFirstItemInSection}" 
                    HeightRequest="1" />
    
                <Label Text="{Binding TrackName}" />
            </VerticalStackLayout>
        </xct:VirtualViewCell>
    </xct:VirtualViewCell>
  3. Install VirtualListView in .NET MAUI

    main

    To use the VirtualListView control in your .NET MAUI application, you must register it in your app builder using the .UseVirtualListView() extension method.

    public static MauiApp Create()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp<App>()
            .UseVirtualListView(); // <--- Register the control
        return builder.Build();
    }
  4. Use built-in adapters: VirtualListViewAdapter and ObservableCollectionAdapter

    main

    The library provides two ready-to-use implementations for common scenarios:

    1. VirtualListViewAdapter<TItem>: A basic adapter backed by an IList<TItem>.
    2. ObservableCollectionAdapter<TItem>: A wrapper for ObservableCollection<TItem> that automatically calls InvalidateData() when the collection changes via the CollectionChanged event.
    // Using VirtualListViewAdapter with a simple list
    var adapter = new VirtualListViewAdapter<string>(new [] {
        "Item 1",
        "Item 2",
        "Item 3"
    });
    
    // Using ObservableCollectionAdapter with an ObservableCollection
    var items = new ObservableCollection<string>();
    items.Add("Item 1");
    var adapter = new ObservableCollectionAdapter<string>(items);
  5. Use DataTemplateSelectors for Items and Sections

    main

    You can use template selectors to choose different DataTemplates based on the item type or position.

    • For Item template selectors, subclass AdapterItemDataTemplateSelector and override SelectItemTemplate.
    • For Section template selectors, subclass AdapterSectionDataTemplateSelector.
    public class MyItemTemplateSelector : AdapterItemDataTemplateSelector
    {
    	PersonTemplate personTemplate = new PersonTemplate();
    	GenericTemplate genericTemplate = new GenericTemplate();
    
    	public override DataTemplate SelectItemTemplate(IVirtualListViewAdapter adapter, int sectionIndex, int itemIndex)
    	{
    		var item = adapter.GetItem(sectionIndex, itemIndex);
    
    		if (item is Person)
    			return personTemplate;
    		
    		return genericTemplate;
    	}
    }
  6. Implement the IVirtualListViewAdapter interface

    main

    The VirtualListView uses an adapter pattern rather than a standard collection. To create a custom data source (e.g., for direct database access), implement the IVirtualListViewAdapter interface. This allows the control to query data on demand, which is optimal for large datasets and avoids in-memory cache invalidation issues.

    public interface IVirtualListViewAdapter
    {
    	int GetNumberOfSections();
    
    	object GetSection(int sectionIndex);
    
    	int GetNumberOfItemsInSection(int sectionIndex);
    
    	object GetItem(int sectionIndex, int itemIndex);
    
    	event EventHandler OnDataInvalidated;
    
    	void InvalidateData();
    }
  7. Handle Scrolled and Empty View states

    main

    Empty View

    If the adapter contains $\le 1$ section and no items, you can define an EmptyView to display:

    <vlv:VirtualListView.EmptyView>
      <Label Text="EMPTY" />
    </vlv:VirtualListView.EmptyView>

    Scrolled Notifications

    Observe scrolling via the ScrolledCommand (which receives ScrolledEventArgs) or the OnScrolled event. The arguments contain the current X/Y scroll position.

  8. Configure Pull-to-Refresh

    main

    Pull-to-refresh is supported on iOS/MacCatalyst and Android. To enable it, set IsRefreshEnabled to true.

    • Use RefreshCommand or subscribe to the OnRefresh event to execute your logic.
    • Use RefreshAccentColor to customize the indicator color.
  9. Configure Selection modes and properties

    main

    The VirtualListView supports three selection modes: None, Single, and Multiple. Note that only Item types are selectable.

    Key properties:

    • SelectedItem: Bindable property for the single selected item.
    • SelectedItems: Bindable property for multiple selected items.
    • OnSelectedItemsChanged: Event fired when the selection changes.