Skip to main content

Load More in .NET MAUI Data Grid

  • 6 minutes to read

DataGridView supports the load-more functionality – when a user scrolls to the bottom of the grid, a set of new data items is added to the end of the grid. To enable this feature, follow these steps:

  1. Set the IsLoadMoreEnabled property to true.
  2. Use one of the following ways to load more data items to the grid after a user scrolls to the last row:

  3. Set the IsRefreshing property to false to notify the grid that the load-more operation is completed and the loading indicator should be hidden.
  4. Use the IndicatorColor property to change the loading indicator color.

Note

Note that the load-more functionality stops and the LoadMore event is not raised if the number of items in the source has not been changed.

Example

In this example, a grid displays a collection of orders (fifty items initially) and loads ten more orders when a user reaches the last row. New orders for each next load are generated randomly in code. The maximum number of loads a user is allowed to perform is 3. The grid displays the number of loaded data items at the bottom.

MAUI Data Grid - Load More

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 adds new items to the grid’s data source when a user reaches the end of the grid, and a property that notifies the grid about the loading activity status.
  • A View that specifies how the grid displays data.

Model

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

  • Product – Specifies products to order. Each product has a name and unit price.
  • Order – Specifies individual orders. Each order has a date, product, and quantity of product units.
  • OrderData – The Orders property of this class returns a collection of Order objects generated randomly in code. This collection serves as a data source for the grid.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace DataGrid_LoadMore {
    public class Product {
        string name;
        int unitPrice;

        public Product(string name, int unitPrice) {
            this.name = name;
            this.unitPrice = unitPrice;
        }
        public string Name {
            get { return name; }
            set { name = value; }
        }
        public int UnitPrice {
            get { return unitPrice; }
            set { unitPrice = value; }
        }
    }

    public class Order {
        DateTime date;
        Product product;
        int quantity;

        public Order() {
            this.date = DateTime.Today;
            this.product = new Product("", 0);
            this.Quantity = 0;
        }
        public Order(DateTime date, Product product, int quantity) {
            this.date = date;
            this.product = product;
            this.quantity = quantity;
        }
        public DateTime Date {
            get { return date; }
            set { date = value; }
        }
        public Product Product {
            get { return product; }
            set { product = value; }
        }
        public int Quantity {
            get { return quantity; }
            set { quantity = value; }
        }
    }

    public class OrderData {
        readonly List<Product> products;
        const int orderCount = 50;
        readonly Random random;
        public ObservableCollection<Order> Orders { get; set; }

        public OrderData() {
            random = new Random((int)DateTime.Now.Ticks);
            products = new List<Product>();
            GenerateProducts();
            Orders = new ObservableCollection<Order>();
            for (int i = 0; i < orderCount; i++)
                Orders.Add(GenerateOrder());
        }

        public void LoadMoreOrders() {
            for (int i = 0; i < 10; i++)
                Orders.Add(GenerateOrder());
        }

        Order GenerateOrder() {
            Order order = new Order(new DateTime(2020, 1, 1).AddDays(random.Next(0, 60)),
                            RandomItem<Product>(products), random.Next(1, 100));
            return order;
        }

        T RandomItem<T>(IList<T> list) {
            int index = (int)(random.NextDouble() * 0.99 * (list.Count));
            return list[index];
        }

        void GenerateProducts() {
            products.Add(new Product("Tofu", 50));
            products.Add(new Product("Chocolade", 34));
            products.Add(new Product("Ikura", 70));
            products.Add(new Product("Chai", 3));
            products.Add(new Product("Boston Crab Meat", 36));
            products.Add(new Product("Ravioli Angelo", 18));
            products.Add(new Product("Ipon Coffee", 10));
            products.Add(new Product("Queso Cabrales", 25));
        }
    }
}

View Model

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

  • Orders – Specifies a collection of orders that is used as the data source for the grid.
  • LoadMoreCommand - Specifies a command that randomly generates ten new orders and adds them to the grid’s data source.
  • IsRefreshing - Specifies whether the the grid displays the loading indicator. When you implement the LoadMoreCommand command, set this property to false after you add new items to the data source.
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Input;
using Microsoft.Maui.Controls;

namespace DataGrid_LoadMore {
    public class ViewModel : INotifyPropertyChanged {
        OrderData data;

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

        ObservableCollection<Order> orders;
        public ObservableCollection<Order> Orders {
            get { return orders; }
            set {
                if (orders != value) {
                    orders = value;
                    OnPropertyChanged("Products");
                }
            }
        }

        LoadMoreDataCommand loadMoreCommand = null;
        public LoadMoreDataCommand LoadMoreCommand {
            get { return loadMoreCommand; }
            set {
                if (loadMoreCommand != value) {
                    loadMoreCommand = value;
                    OnPropertyChanged("LoadMoreCommand");
                }
            }
        }

        public ViewModel() {
            this.data = new OrderData();
            Orders = data.Orders;
            LoadMoreCommand = new LoadMoreDataCommand(ExecuteLoadMoreCommand);
        }

        void ExecuteLoadMoreCommand() {
            Task.Run(() => {
                Thread.Sleep(1000);
                Device.BeginInvokeOnMainThread(() => {
                    data.LoadMoreOrders();
                    Orders = data.Orders;
                    IsRefreshing = false;
                });
            });
        }

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

    public class LoadMoreDataCommand : ICommand {
        readonly Action execute;
        int numOfLoadMore = 0;

        public event EventHandler CanExecuteChanged;

        public LoadMoreDataCommand(Action execute) {
            this.execute = execute;
        }

        public bool CanExecute(object parameter) {
            return numOfLoadMore < 3;
        }

        public void Execute(object parameter) {
            numOfLoadMore++;
            ChangeCanExecute();
            this.execute();
        }
        void ChangeCanExecute() {
            CanExecuteChanged?.Invoke(this, new EventArgs());
        }
    }
}

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 Orders property.
  4. Set the IsLoadMoreEnabled to true to enable the grid’s load-more functionality.
  5. Bind the IsRefreshing and LoadMoreCommand properties to the corresponding properties of the view model.
  6. Use the IndicatorColor property to specify the loading indicator color.
  7. Set the IsRefreshing property false after data is loaded to hide the loading indicator in the grid.
  8. Add columns to the grid and bind them to the Order’s and Product’s properties. You should also specify a Total column that displays each order amount calculated according to the expression: Quantity*Product.UnitPrice.
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="DataGrid_LoadMore.MainPage"
             xmlns:local="clr-namespace:DataGrid_LoadMore"
             xmlns:dxg="clr-namespace:DevExpress.Maui.DataGrid;assembly=DevExpress.Maui.DataGrid">
    <ContentPage.Resources>
        <ResourceDictionary>
            <Style TargetType="Label">
                <Setter Property="Padding" Value="14, 21, 14, 21"/>
                <Setter Property="VerticalOptions" Value="Center"/>
            </Style>
        </ResourceDictionary>
    </ContentPage.Resources>
    <ContentPage.BindingContext>
        <local:ViewModel/>
    </ContentPage.BindingContext>
    <dxg:DataGridView ItemsSource="{Binding Orders}" 
                      IsLoadMoreEnabled="True" 
                      LoadMoreCommand="{Binding LoadMoreCommand}" 
                      IsRefreshing="{Binding IsRefreshing, Mode=TwoWay}" 
                      IndicatorColor="Lime"
                      RowHeight="70">
        <dxg:DataGridView.Columns>
            <dxg:DateColumn FieldName = "Date" DisplayFormat="d"/>
            <dxg:TextColumn FieldName="Product.Name" Caption = "Product" Width = "170"/>
            <dxg:NumberColumn FieldName="Product.UnitPrice" Caption = "Price" DisplayFormat="C0"/>
            <dxg:NumberColumn FieldName="Quantity" />
            <dxg:NumberColumn FieldName="Total" 
                              UnboundType="Integer" UnboundExpression="[Quantity] * [Product.UnitPrice]" 
                              DisplayFormat="C0" IsReadOnly="True"/>
        </dxg:DataGridView.Columns>
        <dxg:DataGridView.TotalSummaries>
            <dxg:GridColumnSummary FieldName="Total" Type="Count"/>
        </dxg:DataGridView.TotalSummaries>
    </dxg:DataGridView>
</ContentPage>

View Example: Implement Load-More Functionality

Infinite Collection View Scrolling

Infinite Collection View Scrolling Featured Scenario