Infinite Scrolling in Collection View for .NET MAUI
- 8 minutes to read
DXCollectionView supports the load-more functionality - when a user scrolls to the bottom (or right, if the Orientation property is set to Horizontal) of the list, a set of new data items is added to the end of the list.
Follow the steps below to enable endless scrolling for a DXCollectionView:
- Set the DXCollectionView.IsLoadMoreEnabled property to true.
- Create a command that is executed when a user scrolls to the bottom of the CollectionView to load more data items and bind it to the LoadMoreCommand property.
-or -
Handle the LoadMore event. - Use the IsRefreshing property to notify the CollectionView that the load-more operation is complete and the loading indicator should be hidden.
- Use the IndicatorColor property to change the loading indicator’s color.
Example
In this example, the CollectionView displays a list of mail messages and loads a set of earlier messages when a user scrolls to the last item.
The application contains:
- A Model that defines application data.
- A View Model with a property that returns a data source for the CollectionView, a command that adds new items to a data source when a user reaches the end of the list, and a property that notifies the CollectionView about the loading activity status.
- A View that specifies how the CollectionView displays data.
Model
Create the following classes that define the application’s data:
- MailData - Instances of this class are individual mail messages. Each message contains the name and email of the sender, subject, message text, and the time when the message was sent (generated randomly in code).
- MailMessageRepository - The MailMessages property of this class returns a collection of MailData objects (a data source for the CollectionView).
using System;
using System.Collections.Generic;
namespace CollectionView_LoadMore {
public class MailData {
public string SenderName { get; set; }
public string SenderEmail { get; set; }
public string Subject { get; set; }
public string Body { get; set; }
public DateTime MailTime { get; set; }
}
public class MailMessageRepository {
public List<MailData> MailMessages { get; private set; }
readonly Random random;
public MailMessageRepository() {
this.random = new Random((int)DateTime.Now.Ticks);
GenerateMessages();
}
public void GenerateMessages() {
MailMessages = new List<MailData>();
MailMessages.Add(
new MailData() {
SenderName = "Nancy Davolio",
SenderEmail = "NancyDavolio@devexpress.com",
Subject = "Choose between PPO and HMO Health Plan",
Body = "We need a final decision on whether we are planning " +
"on staying with a PPO Health Plan or we plan on switching to an HMO. " +
"We cannot proceed with compliance with the Affordable Health Act " +
"until we make this decision. " +
"John Heart: Samantha, I'm still reviewing costs. " +
"I am not in a position to make a final decision at this point.",
}
);
MailMessages.Add(
new MailData() {
SenderName = "Andrew Fuller",
SenderEmail = "AndrewFuller@devexpress.com",
Subject = "Google AdWords Strategy",
Body = "Make final decision on whether we are going " +
"to increase our Google AdWord spend " +
"based on our 2013 marketing plan."
}
);
MailMessages.Add(
new MailData() {
SenderName = "Janet Leverling",
SenderEmail = "JanetLeverling@devexpress.com",
Subject = "New Brochures",
Body = "Review and the new brochure designs and give final approval. " +
"John Heart: we reviewed them all and forwarded an email with all changes we need " +
"to make to the brochures to comply with local regulations."
}
);
MailMessages.Add(
new MailData() {
SenderName = "Margaret Peacock",
SenderEmail = "MargaretPeacock@devexpress.com",
Subject = "Website Re-Design Plan",
Body = "The changes in our brochure designs for 2013 require us " +
"to update key areas of our website."
}
);
MailMessages.Add(
new MailData() {
SenderName = "Steven Buchanan",
SenderEmail = "StevenBuchanan@devexpress.com",
Subject = "Non-Compete Agreements",
Body = "Make final decision on whether our employees " +
"should sign non-compete agreements. " +
"Samantha Bright: Greta, we discussed this and we feel it is unnecessary " +
"to require non-compete agreements for employees."
}
);
MailMessages.Add(
new MailData() {
SenderName = "Michael Suyama",
SenderEmail = "MichaelSuyama@devexpress.com",
Subject = "Update Employee Files with New NDA",
Body = "Management has approved new NDA. " +
"All employees must sign the new NDA and their employee files must be updated. " +
"Greta Sims: Having difficulty with a few employees. Will follow up by Email."
}
);
MailMessages.Add(
new MailData() {
SenderName = "Robert King",
SenderEmail = "RobertKing@devexpress.com",
Subject = "Review Health Insurance Options Under the Affordable Care Act",
Body = "The changes in health insurance laws " +
"require that we review our existing coverage " +
"and update it to meet regulations. Samantha Bright will be point on this. " +
"Samantha Bright: " +
"Update - still working with the insurance company to update our coverages."
}
);
MailMessages.Add(
new MailData() {
SenderName = "Laura Callahan",
SenderEmail = "LauraCallahan@devexpress.com",
Subject = "Give Final Approval for Refunds",
Body = "Refunds as a result of our 2013 TV recalls have been submitted. " +
"Need final approval to cut checks to customers. " +
"Samantha Bright: You can send the checks out mid-May."
}
);
MailMessages.Add(
new MailData() {
SenderName = "Anne Dodsworth",
SenderEmail = "AnneDodsworth@devexpress.com",
Subject = "Decide on Mobile Devices to Use in the Field",
Body = "We need to decide whether we are going " +
"to use Surface tablets in the field or go with iPad. " +
"I have prepared the pros and cons based on feedback from everyone in the IT dept. " +
"Arthur Miller: Surface."
}
);
GenerateRandomMailTime();
}
void GenerateRandomMailTime() {
DateTime currentDate = DateTime.Now.Date.AddMinutes(-1 * this.random.Next(1, 560));
foreach (MailData mail in MailMessages) {
currentDate = currentDate.AddMinutes(-1 * this.random.Next(1, 200));
mail.MailTime = currentDate;
}
}
}
}
View Model
Create the ViewModel class with the following properties to which the CollectionView should be bound:
- ItemSource - a collection of messages sorted in descending order according to when the message was sent.
- LoadMoreCommand - a command that randomly generates earlier messages and adds them to the item source.
- IsRefreshing - specifies whether the loading indicator is displayed in the CollectionView. When you implement the LoadMoreCommand command, set this property to false after you add new messages to the item source.
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Input;
namespace CollectionView_LoadMore{
public class ViewModel : INotifyPropertyChanged {
readonly MailMessageRepository repository;
DateTime currentDate = DateTime.Now;
readonly Random random;
public ViewModel(MailMessageRepository repository) {
this.random = new Random((int)DateTime.Now.Ticks);
this.repository = repository;
LoadMoreCommand = new Command(ExecuteLoadMoreCommand);
}
IList<MailData> itemSource;
public IList<MailData> ItemSource {
get { return itemSource; }
set {
if (itemSource != value) {
itemSource = value;
OnPropertyChanged("ItemSource");
}
}
}
bool isRefreshing = false;
public bool IsRefreshing {
get { return isRefreshing; }
set {
if (isRefreshing != value) {
isRefreshing = value;
OnPropertyChanged("IsRefreshing");
}
}
}
ICommand loadMoreCommand = null;
public ICommand LoadMoreCommand {
get { return loadMoreCommand; }
set {
if (loadMoreCommand != value) {
loadMoreCommand = value;
OnPropertyChanged("LoadMoreCommand");
}
}
}
void ExecuteLoadMoreCommand() {
Task.Run(() => {
Thread.Sleep(1000);
if (ItemSource == null)
ItemSource = new List<MailData>();
foreach (MailData mail in this.repository.MailMessages) {
this.currentDate = this.currentDate.AddMinutes(-1 * this.random.Next(5, 240));
ItemSource.Add(new MailData() {
SenderName = mail.SenderName,
SenderEmail = mail.SenderEmail,
Subject = mail.Subject,
Body = mail.Body,
MailTime = this.currentDate,
});
}
IsRefreshing = false;
});
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "") {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
In the MainPage.xaml.cs file, set the ContentPage’s BindingContext to a ViewModel object.
using Microsoft.Maui.Controls;
namespace CollectionView_LoadMore {
public partial class MainPage : ContentPage {
public MainPage() {
InitializeComponent();
BindingContext = new ViewModel(new MailMessageRepository());
}
}
}
View
In the MainPage.xaml file, create and set up the CollectionView:
- Add a DXCollectionView object to the page.
- Bind the ItemsSource property to the view model‘s ItemSource property.
- Set the IsLoadMoreEnabled to true to enable the CollectionView’s load-more functionality.
Bind the IsRefreshing and LoadMoreCommand properties to the corresponding properties of the view model.
Use the IndicatorColor property to specify the loading indicator color. - Use the ItemTemplate property to define the appearance of list items. Bind the item template’s elements to properties of a MailData object.
<ContentPage
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="CollectionView_LoadMore.MainPage"
xmlns:ios="clr-namespace:Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific;assembly=Microsoft.Maui.Controls"
xmlns:dxcv="clr-namespace:DevExpress.Maui.CollectionView;assembly=DevExpress.Maui.CollectionView"
ios:Page.UseSafeArea="True">
<dxcv:DXCollectionView x:Name="collectionView"
ItemsSource="{Binding ItemSource}"
IsLoadMoreEnabled="True"
IsRefreshing="{Binding IsRefreshing, Mode=TwoWay}"
LoadMoreCommand="{Binding LoadMoreCommand}"
IndicatorColor="Lime">
<dxcv:DXCollectionView.ItemTemplate>
<DataTemplate>
<Grid ColumnSpacing="16" Padding="16, 14">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackLayout Grid.Column="0"
Orientation="Vertical"
VerticalOptions="StartAndExpand">
<Label Text="{Binding SenderName}"
MaxLines="1"
FontAttributes="Bold"
TextColor="#55575c"/>
<Label Text="{Binding Subject}"
MaxLines="1"
TextColor="#55575c"/>
<Label Text="{Binding Body}"
MaxLines="1"
TextColor="#959aa0"/>
</StackLayout>
<StackLayout Grid.Column="1"
Orientation="Vertical"
VerticalOptions="StartAndExpand">
<Label TextColor="#959aa0"
FontSize="Small"
Text="{Binding MailTime, StringFormat='{0:ddd MM/dd}'}"/>
<Label TextColor="#959aa0"
FontSize="Small"
Text="{Binding MailTime, StringFormat='{0:hh:mm tt}'}"/>
</StackLayout>
</Grid>
</DataTemplate>
</dxcv:DXCollectionView.ItemTemplate>
</dxcv:DXCollectionView>
</ContentPage>