Skip to main content

Pull-to-Refresh in Collection View for .NET MAUI

  • 7 minutes to read

You can set up DXCollectionView to allow users to request content updates with a pull-down gesture.

DevExpress Collection View for .NET MAUI - Pull to refresh

Follow the steps below to pull-to-refresh functionality for a DXCollectionView:

  1. Set the IsPullToRefreshEnabled property to true.
  2. Create a command that is executed when a user pulls the CollectionView down and bind it to the PullToRefreshCommand property.
    -or-
    Handle the PullToRefresh event.
  3. Use the IsRefreshing property to notify the CollectionView that the pull-to-refresh operation is complete and the refresh indicator should be hidden.
  4. For iOS only, the IndicatorColor property allows you to change the refresh indicator’s color.

Example

In this example, the CollectionView displays a list of incoming messages that a user can pull down to check for new messages.

View Example: CollectionView - Pull-to-Refresh

Collection View - Pull to Refresh

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 updates a data source on the pull-down gesture, and a property that notifies the CollectionView about the refresh 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_PullToRefresh {
    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.
  • PullToRefreshCommand - a command that updates the item source.
  • IsRefreshing - specifies whether the refresh indicator is displayed in the CollectionView. When you implement the PullToRefreshCommand command, set this property to false after you assign a new message collection to the ItemSource property.
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Input;
using Microsoft.Maui.Controls;

namespace CollectionView_PullToRefresh {
    public class ViewModel : INotifyPropertyChanged {
        readonly MailMessageRepository repository;

        public ViewModel(MailMessageRepository repository) {
            this.repository = repository;
            ItemSource = GetSortedMessages(this.repository);
            PullToRefreshCommand = new Command(ExecutePullToRefreshCommand);
        }

        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 pullToRefreshCommand = null;
        public ICommand PullToRefreshCommand {
            get { return pullToRefreshCommand; }
            set {
                if (pullToRefreshCommand != value) {
                    pullToRefreshCommand = value;
                    OnPropertyChanged("PullToRefreshCommand");
                }
            }
        }

        void ExecutePullToRefreshCommand() {
            Task.Run(() => {
                Thread.Sleep(1000);
                this.repository.GenerateMessages();
                ItemSource = GetSortedMessages(this.repository);
                IsRefreshing = false;
            });
        }

        IList<MailData> GetSortedMessages(MailMessageRepository repository) {
            return repository.MailMessages.OrderByDescending(x => x.MailTime).ToList();
        }

        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_PullToRefresh {
    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:

  1. Add a DXCollectionView object to the page.
  2. Bind the ItemsSource property to the view model‘s ItemSource property.
  3. Set the IsPullToRefreshEnabled to true to enable the CollectionView’s pull-to-refresh functionality.
    Bind the IsRefreshing and PullToRefreshCommand properties to the corresponding properties of the view model.
    Use the IndicatorColor property to specify the refresh indicator color (iOS only).
  4. 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_PullToRefresh.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}"
                           IsPullToRefreshEnabled="True"
                           IsRefreshing="{Binding IsRefreshing, Mode=TwoWay}"
                           PullToRefreshCommand="{Binding PullToRefreshCommand}"
                           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="7"
                               TextColor="#959aa0"/>
                    </StackLayout>
                    <Label Grid.Column="1"
                           Text="{Binding MailTime, StringFormat='{0:HH:mm}'}"
                           TextColor="#959aa0"
                           VerticalOptions="StartAndExpand" />
                </Grid>
            </DataTemplate>
        </dxcv:DXCollectionView.ItemTemplate>
    </dxcv:DXCollectionView>
</ContentPage>