• Stars
    star
    226
  • Rank 176,452 (Top 4 %)
  • Language
    C#
  • License
    MIT License
  • Created almost 4 years ago
  • Updated 5 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A slim ListView implementation for .NET MAUI that uses Platform virtualized lists / collections

VirtualListView for .NET MAUI

This is an experiment in creating a virtualized ListView control for .NET MAUI to support simple, fast, multi-templated, uneven item sized lists by not adding too many bells and whistles and using an adapter pattern data source.

Nuget: Redth.Maui.VirtualListView

Vroooom!

VirtualListView-Maui-MacCatalyst

In the sample, each item (and header/footer) is measured as it is recycled. Performance is pretty great considering! In the future there will be an option to tell the ListView if your template(s) are a consistent size so that the measure can be skipped for even better performance.

Native controls

The implementation uses fast native controls in its renderers and optimizes for the native platform's recycling strategies. Items are cached through the platform's recycling mechanisms so that they can be reused efficiently. This also means the MAUI representation of items are cached as well. Each type of template (Item, Section Header, Section Footer) is cached individually so that they are reused efficiently.

Controls used on each platform:

  • iOS: UICollectionView
  • Android: RecyclerView
  • WinAppSDK: ItemsRepeaterScrollHost with IElementFactory

Setup

To add the Virtual List View control to your project, you need to add .UseVirtualListView() to your app builder:

		public static MauiApp Create()
		{
			var builder = MauiApp.CreateBuilder();
			builder
				.UseMauiApp<App>()
				.UseVirtualListView(); // <--- THIS
			return builder.Build();
		}

Windows App SDK 1.3.*

There is a bug that is fixed in Windows App SDK 1.3.x and newer with IElementFactory. You will need to explicitly specify a newer version of Microsoft.WindowsAppSdk NuGet package to use than is implicitly referenced by .NET MAUI at this time:

<ItemGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">
  <PackageReference Include="Microsoft.WindowsAppSdk" Version="1.3.230602002" />
</ItemGroup>

Adapter / Data Source

Instead of starting with a typical C# collection such as ObservableCollection, the VirtualListView takes the adapter approach that is common to iOS and Android has the concept of grouping built in (called Sections).

This pattern is optimal since it allows for easily creating adapters backed by direct access data stores such as databases. Instead of trying to load data from the actual datastore, and trying to deal with cache invalidation for an in memory collection you can write your adapter directly against any type of storage.

To create an adapter for the VirtualListView, you need to implement the following interface:

public interface IVirtualListViewAdapter
{
	int GetNumberOfSections();

	object GetSection(int sectionIndex);

	int GetNumberOfItemsInSection(int sectionIndex);

	object GetItem(int sectionIndex, int itemIndex);

	event EventHandler OnDataInvalidated;

	void InvalidateData();
}

There are a few implementations included in the box to use:

VirtualListViewAdapter

This is a basic adapter backed by an IList<TItem>:

var adapter = new VirtualListViewAdapter<string>(
	new [] {
		"Item 1",
		"Item 2",
		"Item 3",
		//...
	});

ObservableCollectionAdapter

Many developers are accustomed to using ObservableCollection. While I recommend against using this if possible, there is a built in adapter that takes in an ObservableCollection<TItem> instance to help map it to the adapter pattern:

var items = new ObservableCollection<string>();
items.Add("Item 1");
items.Add("Item 2");

var adapter = new ObservableCollectionAdapter<string>(items);

items.Add("Item 3");

When using this adapter, the adapter will automatically invalidate itself (by calling this.InvalidateData() when the collection changes via the CollectionView.CollectionChanged event).

Custom Adapter

For many scenarios, it is ideal to create your own adapter implementation. You can implement the IVirtualListViewAdapter directly, or subclass VirtualListViewAdapterBase<TSection, TItem>.

Here's an example of a custom adapter for a flat list (no sections/grouping). Notice that we cache commonly used data such as ItemsForSection and we will reset the cache anytime the data is invalidated:

public class SQLiteAdapter : VirtualListViewAdapterBase<object, ItemInfo>
{
	public SQLiteAdapter() : base()
	{
		Db = new Database(...);
	}

	public Database Db { get; }

	int? cachedItemCount = null;

	// No sections/grouping, so disregard the sectionIndex
	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()
	{
		// Clear our item count cache
		// Also do this any time we may insert or delete data 
		cachedItemCount = null;
		base.InvalidateData();
	}
}

Here's an example of a more sophisticated adapter with grouping/sections. Again, notice we cache Section count and Item count per section:

public class SQLiteSectionedAdapter : VirtualListViewAdapterBase<GroupInfo, ItemInfo>
{
	public SQLiteSectionedAdapter() : base()
	{
		Db = new Database(...);
	}

	public Database Db { get; }

	Dictionary<int, GroupInfo> cachedSectionSummaries = new ();

	int? cachedNumberOfSections = null;

	public int GetNumberOfSections()
		=> cachedNumberOfSections ??= Db.ExecuteScalar<int>("SELECT DISTINCT COUNT(GroupId) FROM Items");

	// No sections/grouping, so disregard the sectionIndex
	public override int GetNumberOfItemsInSection(int sectionIndex)
		=> cachedItemCount ??= Db.ExecuteScalar<int>("SELECT COUNT(Id) FROM Items");

	public GroupInfo GetSection(int sectionIndex)
	{
		if (cachedSectionSummaries.ContainsKey(sectionIndex))
			return cachedSectionSummaries[sectionIndex];

		var sql = @"
				SELECT DISTINCT g.GroupId, g.GroupName, Count(i.Id) as ItemCount
				FROM Items g
					INNER JOIN Items i ON i.GroupId = g.GroupId
				GROUP BY g.GroupId
				ORDER BY g.GroupName
				LIMIT 1 OFFSET ?
		";

		var groupInfo = Db.FindWithQuery<GroupInfo>(sql, sectionIndex);

		if (groupInfo != null)
			cachedSectionSummaries.Add(sectionIndex, groupInfo);

		return groupInfo;
	}

	public override string GetItem(int sectionIndex, int itemIndex)
		=> Db.FindWithQuery<ItemInfo>("SELECT * FROM Items WHERE GroupId=? ORDER BY Id LIMIT 1 OFFSET ?", sectionIndex, itemIndex);

	public override void InvalidateData()
	{
		// Clear our caches
		// Also do this any time we may insert or delete data 
		cachedItemCount = null;
		cachedNumberOfSections.Clear();

		base.InvalidateData();
	}
}

Templates

DataTemplates are available for Items along with an Item Template Selector (which is a custom class based on the adapter pattern to select templates).

Different templates can be specified for:

  • Global Header
  • Global Footer
  • Section Header
  • Section Footer
  • Item

In addition, it's possible to use Template Selectors which allow you to use different DataTemplates depending on the section / item index being displayed.

Template selectors are available for:

  • Section Headers and Footers
  • Items

For Item template selectors, sublcass the AdapterItemDataTemplateSelector:

public class MyItemTemplateSelector
{
	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;
	}
}

For section template selectors, subclass AdapterSectionDataTemplateSelector.

Virtual ViewCells

All templates can contain a single IView, or alternatively you can use VirtualViewCell to wrap your view.

The VirtualViewCell's ResourceDictionary will contain a set of values which are are useful for adapting your views for things like separators and selection state:

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

NOTE: These are also available as properties on VirtualViewCell itself, since it implements IPositionInfo

You can access these properties from your templates. Here's an example of displaying an item separator using these properties, as well as changing the background color based on the selection state and a converter:

<?xml version="1.0" encoding="UTF-8"?>
<xct:VirtualViewCell
	xmlns:xct="clr-namespace:Microsoft.Maui.Controls;assembly=VirtualListView"
	xmlns="http://xamarin.com/schemas/2014/forms" 
	xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
	x:Class="VirtualListViewSample.GenericViewCell">
  <xct:VirtualViewCell>
		<VerticalStackLayout
			Spacing="0"
			BackgroundColor="{Binding Source={x:Reference self}, Path=IsSelected, Converter={StaticResource selectedColorConverter}}">

			<BoxView
				HorizontalOptions="FillAndExpand"
				HeightRequest="1"
				BackgroundColor="#f8f8f8"
				IsVisible="{DynamicResource IsNotFirstItemInSection}" <!-- Use the automatic property -->
 				/>

			<Border Background="#f0f0f0" StrokeShape="{RoundedRectangle CornerRadius=14}" Margin="10,5,10,5" Padding="10">
				<Label Text="{Binding TrackName}" />
			</Border>

		</VerticalStackLayout>
	</xct:VirtualViewCell>
</xct:VirtualViewCell>

Notice the IsVisible="{DynamicResource IsNotFirstItemInSection}" references a resource which has been automatically populated by the VirtualViewCell.

Selection

There are 3 selection modes: None, Single, and Multiple. Only Item types are selectable.

There are SelectedItem and SelectedItems bindable properties. There's an OnSelectedItemsChanged event fired whenever these change.

Refreshing

Pull to refresh is enabled for iOS/MacCatalyst and Android. WindowsAppSDK does not have the equivalent feature so there is no support for it. You can use the RefreshCommand or subscribe to the OnRefresh event to perform your logic while the refresh indicator displays. You must set IsRefreshEnabled to true to enable the gesture.
You can also set the RefreshAccentColor to change the color of the refresh indicator.

Empty View

If your adapter has <= 1 section and no items, an empty view can be displayed automatically:

<vlv:VirtualListView.EmptyView>
  <Grid>
    <Label HorizontalOptions="Center" VerticalOptions="Center" Text="EMPTY" />
  </Grid>
</vlv:VirtualListView.EmptyView>

Scrolled

Scrolled notifications can be observed with ScrolledCommand which will pass a ScrolledEventArgs parameter, or the OnScrolled event with a parameter of the same type. The event args contain the X/Y position scrolled.

Future

Looking ahead, there are a few goals:

  1. Even Rows - by default every cell is assumed uneven and measured every time the context changes or the cell is recycled. Adding an option to assume each template type is the same size will make performance even better, but will be an explicit opt-in
  2. Supporting "size of content" constraints

Some current non-goals but considerations for even later:

  • Grid / Column support
  • Sticky section headers

More Repositories

1

PushSharp

A server-side library for sending Push Notifications to iOS (iPhone/iPad APNS), Android (C2DM and GCM - Google Cloud Message), Windows Phone, Windows 8, Amazon, Blackberry, and (soon) FirefoxOS devices!
C#
4,362
star
2

ZXing.Net.Mobile

Barcode Scanner for Xamarin.iOS, Xamarin.Android, UWP and Tizen
C#
1,067
star
3

dotnet-maui-check

.NET MAUI Check tool
C#
474
star
4

ZXing.Net.Maui

Barcode Scanning for MAUI?
C#
416
star
5

ResizetizerNT

Add SVG's and PNG's to your shared Xamarin Project
C#
319
star
6

APNS-Sharp

Apple Push Notification & Feedback Services Client C# Library
C#
255
star
7

FlamedTVLauncher

A replacement home launcher for Amazon FireTV
C#
189
star
8

HttpTwo

A basic C# HTTP/2 client library
JavaScript
117
star
9

Maui.UITesting

Experimenting with UI Testing approaches for .NET / MAUI
C#
103
star
10

MAUI.AppLinks.Sample

Sample .NET MAUI app which supports deep linking
C#
54
star
11

AndroidSdk.Tools

.NET Library + global tool for various Android SDK Manager, ADB, AVD, Emulator commands
C#
52
star
12

Microsoft.Maui.Platform.Channels

A simple bridge for messaging between .NET and iOS/MacCatalyst/Android Platforms at runtime
C#
51
star
13

Resizetizer

Easily automateable Image resizing for your Xamarin.iOS and Xamarin.Android app assets!
C#
47
star
14

MSSolutionLauncher

Launch multiple instances of Visual Studio for Mac and/or Xamarin Studio easily!
C#
46
star
15

FlatUI.Xamarin.Android

FlatUI Port for Xamarin.Android of CengaLabs FlatUI Kit by @eluleci
C#
46
star
16

C2DM-Sharp

C# Client and Server Libraries for Android C2DM (Cloud 2 Device Messaging)
C#
42
star
17

Xamarin.Binding.Helpers

MSBuild Tasks to help with Binding projects for Xamarin Android and iOS
C#
40
star
18

Android.Signature.Tool

Simple GUI tool for Mac and Windows to help find the SHA1 and MD5 hashes of your Android keystore's and apk's
C#
38
star
19

AppleDev.Tools

.NET Library with useful Apple/Xcode tool wrappers and implementations for developers
C#
37
star
20

MonoDroid.UrlImageViewHelper

C# / Mono for Android Port of koush's UrlImageViewHelper
C#
35
star
21

Xamarin.AppleSignIn.Sample

A sample of how to implement Apple Sign In in Xamarin.Forms for Android, iOS, and UWP
C#
32
star
22

WshLst

Wish List App for Xamarin Developer Showdown
C#
32
star
23

Microsoft.Maui.Icons

Simple project to extract glyphs from icon fonts and generate consts for their names and unicode values
C#
23
star
24

LogcatSharp

ADB Logcat viewer written in C# using Winforms
C#
22
star
25

GCMSharp

Google Cloud Messaging Client & Server Libraries ported to C#
C#
22
star
26

GCM.Client

[DEPRECATED] Add Google Cloud Messaging to your Xamarin.Android application
C#
22
star
27

PassKitSharp

C# / .NET Library for Reading and Writing PassKit files
C#
21
star
28

XamarinEncapsulateNativeSample

An example of how to encapsulate native SDK's with easy bindings to simple API's
C#
18
star
29

xf-to-maui

C#
16
star
30

Xamarin.AspNetCore.Authentication

Add authentication to your Xamarin apps using Asp.Net Core's built in authentication and providers
C#
15
star
31

JabbRIsMobile

MvvmCross based set of mobile apps for JabbR
C#
14
star
32

EggsToGo

Xamarin Cross Platform mobile library for implementing Easter Eggs!
C#
13
star
33

AppStoreConnectNet

.NET Client Library for Apple's AppStoreConnect API's
C#
12
star
34

Maui.Generators.Prototype

A playground with some conceptual generators that could be used in Maui / Single Project
C#
12
star
35

MonoTouch.UrlImageStore

A Url Image Store for Lazy Loading Images
C#
11
star
36

MavenNet

A C# Client for inspecting and interacting with Maven Repositories
C#
11
star
37

XamarinMultiWindowTesting

C#
11
star
38

Plugin.Maui.PoppedContentView

Simple popup control & service for .NET MAUI
C#
11
star
39

iOS.BackgroundFetch.Sample

Xamarin.iOS Recipe for implementing Background Fetching
C#
9
star
40

MonoDroid.UrlImageStore

A Url Image Store for Lazy Loading Images for MonoDroid
C#
9
star
41

Xamarin.AndroidBinderator

An engine to generate Xamarin Binding projects from Maven repositories with a JSON config and razor templates
C#
9
star
42

MauiExceptionsGottaCatchEmAll

Playing with exception handling in MAUI
C#
8
star
43

XamarinSingleProjectConcept

Working area for development a concept of what Single Projects could look like for Xamarin in net6
C#
8
star
44

NestSharp

A Portable NEST API Client for C#/.NET
C#
7
star
45

Google.Auth

Google OAuth Library for C#
C#
7
star
46

Svg2VectorDrawable.Net

C# port of Android Studio's Svg2Vector code
C#
7
star
47

Xamarin.Android.Xposed

Xamarin bindings for the Android Xposed client
C#
7
star
48

Plugin.SocialAuth

Xamarin Plugin to assist with Native social authentication
C#
6
star
49

XamarinStudio.RedthsAddin

Redth's Addins. Extensions for MonoDevelop/Xamarin Studio to improve productivity.
C#
6
star
50

MonoTouch.ImageGallery

Image Gallery View Controller
6
star
51

Xamarin.Wear.WatchFace

An Android Wear watch face made with Xamarin
C#
6
star
52

MAUI.Slim.Bindings

Examples of Slim binding patterns for use within .NET MAUI projects
5
star
53

MapboxSlimBindingDemo

Java
5
star
54

Wikitude.Xamarin

Xamarin.iOS and Xamarin.Android Bindings for Wikitude SDK
JavaScript
5
star
55

MavenRepoBrowser

Xamarin.Forms app for browsing a Maven Repo (Google's)
C#
4
star
56

GitHubReleaseNotesGenerator

A simple git/github based release notes generator
C#
4
star
57

Techorama-2014

My code, slides, etc. From Techorama 2014
C#
4
star
58

Talks

Objective-C
4
star
59

ZxingSharp.Mobile

Renamed to ZXing.Net.Mobile
4
star
60

Xamarin.ChromeCustomTabs

How to use Chrome Custom Tabs in your Xamarin.Android apps!
C#
4
star
61

UnitTests.HeadlessRunner

A platform agnostic netstandard20 unit test runner
C#
4
star
62

ImapiNet

IMAPI .NET Wrapper
C#
3
star
63

MauiIssueReproBrowser

C#
3
star
64

MauiAppCIPlayground

Just a place to play with building maui apps via CI
C#
3
star
65

PushSharp.ClientSamples

Xamarin Client Samples for registering for Push notifications
C#
3
star
66

PptxNotes

Easily export notes from a .pptx to .md and Import into your .pptx from .md
C#
3
star
67

MsiSneakAttack

List and remove those hard to clean .MSI installs!
C#
3
star
68

MauiCollectionViewGallery

Some usages of CollectionView in a MAUI app to test bugs / performance against
C#
3
star
69

dotNUT

.NET implementation of the NUT protocol
C#
3
star
70

Maui.GoogleMobileAds.iOS

iOS Bindings for Google's Mobile Ads SDK to use in .NET MAUI
C#
3
star
71

Maui.Onboarding

Docs, scripts, helpers, and more to configure your environment easily for .NET MAUI development
PowerShell
2
star
72

Xamarin.PlatformMessaging

C#
2
star
73

JdwpDotNet

JDWP (Java Debug Wire Protocol) Implementation in .NET
C#
2
star
74

FbSharp.MonoTouch.Authorization

MonoTouch Facebook Authorization Controller
2
star
75

Xamarin.CI

Simple code to test Continuous Integration
C#
2
star
76

NearbyMonkey

A sample app showing off the Nearby Messages API from Google
C#
2
star
77

Xamarin.PlatformMessaging.Sample

A Sample app using Platform Messaging features
C#
2
star
78

NuGetXamarinCompatibility

Test project to test Microsoft NuGet Packages (HTTP Client, BCL, BCL.Build) compatibility with Xamarin.Android, Xamarin.iOS, and Xamarin PCL projects
C#
2
star
79

Cake.Xamarin.Build

Cake addin for Xamarin product builds
C#
2
star
80

TwilioXamarinBindings

Twilio Bindings for Xamarin apps
C#
2
star
81

Xamarin.HttpClient.Repro

C#
2
star
82

Xappium.Maui.Playground

Just a repo to start testing Xappium UITesting with MAUI apps
C#
2
star
83

MauiEdgeToEdge

Playing around with edge to edge apps and safe areas
C#
2
star
84

FbSharp

C# Library for Facebook Graph API
1
star
85

Zxing

Zxing pull from subversion
1
star
86

f50

C#
1
star
87

GoogleSignInMigration

Sample Xamarin.iOS showing how to use the new Google SignIn API's with the old Plus API's
C#
1
star
88

redth.github.com

Redth
1
star
89

Plugin.Maui.Dev

A helpful package for .NET MAUI Plugin authors to make their dev experience more delightful
1
star
90

CvPlayground

Testing UICollectionView for learnings to apply to MAUI VirtualListView
C#
1
star
91

StreamDeck.Sp108e

Stream Deck Plugin for the SP108E WiFi LED Controller
HTML
1
star
92

homebridge-poolmath-automation

HomeBridge Plugin for PoolMath Automation Controller
TypeScript
1
star
93

CorsProxy

Simple CORS Proxy intended to help with development of WebAssembly apps
C#
1
star
94

AndroidSupportComponents

C#
1
star
95

xUnit.ResultWriter

Write xUnit v2 XML Result files without xUnit
C#
1
star
96

SingleAppEntryPointExperiments

C#
1
star
97

Cake.MonoApiTools

Mono API Tools (Info, Diff, HTML Diff)
C#
1
star
98

ZXing-bindings

MonoTouch bindings for the ZXing for iPhone
C#
1
star
99

VsCodeDotNetInterop

A nice example of using a dotnet exe from VSCode to invoke commands and return results
C#
1
star
100

try_git

1
star