Skip to main content

Pull to Refresh in .NET MAUI Data Grid

  • 5 minutes to read

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

  1. Set the IsPullToRefreshEnabled property to true.
  2. Use one of the following ways to specify data the grid should display after a user pulls it down:

  3. Use the IsRefreshing property to notify the grid that the pull-to-refresh operation is completed and the refresh indicator should be hidden.

Example

In this example, a grid displays a collection of product categories refreshed each time when a user pulls the grid down.

MAUI Data Grid - 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 grid, a command that updates the grid’s data source on the pull-down gesture, and a property that notifies the grid about the refresh activity status.
  • A View that specifies how the grid displays data.

Model

Create the following classes that define the application’s data:

  • ProductCategory – Instances of this class are individual categories of products. Each category contains the name, image, and description.
  • ProductData – The AvailableProductCategories property of this class returns a collection of ProductCategory objects. This collection is populated randomly in code and serves as a data source for the grid.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Microsoft.Maui.Controls;

namespace DataGrid_PullToRefresh {
    public class ProductCategory {
        string name;
        string resourceName;

        public string Name {
            get { return name; }
            set {
                name = value;
                if (Photo == null)
                {
                    resourceName = "DataGrid_PullToRefresh.Images." + value.Replace("/", " ") + ".png";
                    if (!String.IsNullOrEmpty(resourceName))
                        Photo = ImageSource.FromResource(resourceName);
                }
            }
        }
        public ProductCategory(string name) {
            this.Name = name;
        }
        public ImageSource Photo { get; set; }
        public string Description { get; set; }
    }

    public class ProductData {
        readonly List<ProductCategory> allProductCategories;
        public ObservableCollection<ProductCategory> AvailableProductCategories { get; set; }

        public ProductData() : base() {
            this.allProductCategories = new List<ProductCategory>();

            GenerateAllProductCategories();

            int index = new Random().Next(0, 5);
            AvailableProductCategories = GenerateAvailableProductCategories(index);
        }
        ObservableCollection<ProductCategory> GenerateAvailableProductCategories(int number) {
            ObservableCollection<ProductCategory> availableProductCategories = 
                                                  new ObservableCollection<ProductCategory>();
            for (int i = 0; i < 4; i++)
                availableProductCategories.Add(allProductCategories[number + i]);
            return availableProductCategories;
        }

        public void RefreshProductCategories() {
            int index = new Random().Next(0, 5);
            AvailableProductCategories = GenerateAvailableProductCategories(index);
        }

        void GenerateAllProductCategories() {
            allProductCategories.Add(new ProductCategory("Beverages") 
                    {Description = "Soft drinks, coffees, teas, beers, and ales"});
            allProductCategories.Add(new ProductCategory("Condiments") 
                    { Description = "Sweet and savory sauces, relishes, spreads, and seasonings"});
            allProductCategories.Add(new ProductCategory("Confections") 
                    { Description = "Desserts, candies, and sweet breads" });
            allProductCategories.Add(new ProductCategory("Dairy Products") { Description = "Cheeses" });
            allProductCategories.Add(new ProductCategory("Grains/Cereals") 
                    { Description = "Breads, crackers, pasta, and cereal" });
            allProductCategories.Add(new ProductCategory("Meat/Poultry") { Description = "Prepared meats" });
            allProductCategories.Add(new ProductCategory("Produce") 
                    { Description = "Dried fruit and bean curd" });
            allProductCategories.Add(new ProductCategory("Seafood") { Description = "Seaweed and fish" });
        }
    }
}

View Model

Create the ViewModel class with the following properties to which the grid should be bound:

  • ProductCategories – Specifies a collection of product categories that is used as the data source for the grid.
  • PullToRefreshCommand – Specifies a command that updates the grid’s data source.
  • IsRefreshing – Specifies whether the the grid displays the refresh indicator. When you implement the PullToRefreshCommand command, set this property to false after you assign a new collection of product categories to the ProductCategories property.
using Microsoft.Maui.Controls;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Input;

namespace DataGrid_PullToRefresh {
    public class ViewModel : INotifyPropertyChanged {
        ProductData data;

        bool isRefreshing = false;
        public bool IsRefreshing {
            get { return isRefreshing; }
            set {
                if (isRefreshing != value) {
                    isRefreshing = value;
                    OnPropertyChanged("IsRefreshing");
                }
            }
        }

        ObservableCollection<ProductCategory> productCategories;
        public ObservableCollection<ProductCategory> ProductCategories {
            get { return productCategories; }
            set {
                if (productCategories != value) {
                    productCategories = value;
                    OnPropertyChanged("ProductCategories");
                }
            }
        }

        ICommand pullToRefreshCommand = null;
        public ICommand PullToRefreshCommand {
            get { return pullToRefreshCommand; }
            set {
                if (pullToRefreshCommand != value) {
                    pullToRefreshCommand = value;
                    OnPropertyChanged("PullToRefreshCommand");
                }
            }
        }

        public ViewModel() {
            this.data = new ProductData();
            ProductCategories = data.AvailableProductCategories;
            PullToRefreshCommand = new Command(ExecutePullToRefreshCommand);
        }

        void ExecutePullToRefreshCommand() {
            Task.Factory.StartNew(() => {
                Thread.Sleep(3000);
                Device.BeginInvokeOnMainThread(() => {
                    data.RefreshProductCategories();
                    ProductCategories = data.AvailableProductCategories;
                    IsRefreshing = false;
                });
            });
        }

        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string name) {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(name));
        }
    }
}

View

Create and configure a grid in the MainPage.xaml file:

  1. Set the ContentPage’s BindingContext property to a ViewModel object.
  2. Add a DataGridView object to the page.
  3. Bind the ItemsSource property to the view model‘s ProductCategories property.
  4. Set the IsPullToRefreshEnabled to true to enable the grid’s pull-to-refresh functionality.
    Bind the IsRefreshing and PullToRefreshCommand properties to the corresponding properties of the view model.
  5. Add columns (ImageColumn and TemplateColumn) to the grid and bind them to ProductCategory‘s properties.
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="DataGrid_PullToRefresh.MainPage"
             xmlns:dxg="clr-namespace:DevExpress.Maui.DataGrid;assembly=DevExpress.Maui.DataGrid"
             xmlns:local="clr-namespace:DataGrid_PullToRefresh">
    <ContentPage.BindingContext>
        <local:ViewModel/>
    </ContentPage.BindingContext>
    <dxg:DataGridView ItemsSource="{Binding ProductCategories}" IsReadOnly="True"
                      IsPullToRefreshEnabled="true" 
                      PullToRefreshCommand="{Binding PullToRefreshCommand}" 
                      IsRefreshing="{Binding IsRefreshing, Mode=TwoWay}">
        <dxg:DataGridView.Columns>
            <dxg:ImageColumn FieldName="Photo" Width="170" 
                             VerticalContentAlignment="Center" />
            <dxg:TemplateColumn FieldName="Name">
                <dxg:TemplateColumn.DisplayTemplate>
                    <DataTemplate>
                        <Grid BindingContext="{Binding Source}" VerticalOptions="Center" Padding="10,0,0,0">
                            <Grid.RowDefinitions>
                                <RowDefinition Height="Auto" />
                                <RowDefinition Height="Auto" />
                            </Grid.RowDefinitions>
                            <Label Text="{Binding Item.Name}" FontSize="18" FontAttributes="Bold"/>
                            <Label Text="{Binding Item.Description}" Grid.Row="1" FontSize="12"/>
                        </Grid>
                    </DataTemplate>
                </dxg:TemplateColumn.DisplayTemplate>
            </dxg:TemplateColumn>
        </dxg:DataGridView.Columns>
    </dxg:DataGridView>
</ContentPage>

View Example: Implement Pull-to-Refresh