0

I have been working on a project based on C# WinUI 3, I have a ListView in my application which loads a list of data from the database using MySQL. since there is more thank 10k data I have to implement incremented loading in my application. I tried implementing the code from WinUI3 : Add contents during runtime to ListView during scroll, When ever I open the application it initially loads only the give number of data, but when I navigate to another page and come back, the page loads the entire data (10k). Here is the video of the related issue:Demo Video

This is the code that I have used:

Course.cs

using System;

namespace Fees_DBASC.Models.DataModels
{
    public class Course
    {
        public Course() { }

        public Course(int id, string name, int semesters, DateTime timestamp)
        {
            Id = id;
            Name = name;
            Semesters = semesters;
            Timestamp = timestamp;
        }

        public int Id
        {
            get;
            set;
        }

        public string Name
        {
            get;
            set;
        }

        public int Semesters
        {
            get;
            set;
        }

        public DateTime Timestamp
        {
            get;
            set;
        }
    }
}

CourseIncrementalSource.cs

using CommunityToolkit.Common.Collections;
using Fees_DBASC.Core.Database;
using Fees_DBASC.Models.DataModels;
using MySql.Data.MySqlClient;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace Fees_DBASC.Data.Sources
{
    public class CourseIncrementalSource : IIncrementalSource<Course>
    {
        public CourseIncrementalSource() { }

        public async Task<IEnumerable<Course>> GetPagedItemsAsync(int pageIndex, int pageSize, CancellationToken cancellationToken = default)
        {
            List<Course> items = new List<Course>();

            // Establish a connection to the MySQL database
            string connectionString = GlobalDatabaseConfiguration.Url;
            using (MySqlConnection connection = new MySqlConnection(connectionString))
            {
                await connection.OpenAsync();

                // Create a MySQL command to retrieve the items
                MySqlCommand command = connection.CreateCommand();
                command.CommandText = "SELECT * FROM courses ORDER BY id LIMIT @startIndex, @pageSize";
                command.Parameters.AddWithValue("@startIndex", pageIndex * pageSize);
                command.Parameters.AddWithValue("@pageSize", pageSize);

                // Execute the command and retrieve the data
                using (MySqlDataReader reader = (MySqlDataReader)await command.ExecuteReaderAsync())
                {
                    while (await reader.ReadAsync())
                    {
                        // Map the data to a MyDataItem object
                        Course item = new Course();
                        item.Id = reader.GetInt32(0);
                        item.Name = reader.GetString("name");

                        items.Add(item);
                    }
                }
            }
            return items;
        }
    }
}

Courses.cs (A WinUi3 page)

using CommunityToolkit.WinUI;
using Fees_DBASC.Data.Sources;
using Fees_DBASC.Models.DataModels;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Input;

namespace Fees_DBASC.Views.Pages
{
    public sealed partial class Courses : Page
    {

        public Courses()
        {
            this.InitializeComponent();
            this.Loaded += Courses_Loaded;
        }

        private void Courses_Loaded(object sender, RoutedEventArgs e)
        {
            var collection = new IncrementalLoadingCollection<CourseIncrementalSource, Course>(25, null, null, null);
            CourseList.ItemsSource = collection;
        }

        private void OpenContextMenu(object sender, RightTappedRoutedEventArgs e)
        {
            ListView listView = (ListView)sender;
            bool singleItemSelected = (listView.SelectedItems.Count == 1);
            Delete.IsEnabled = (listView.SelectedItems.Count > 0);
            Edit.IsEnabled = singleItemSelected;

            ContextMenu.ShowAt(listView, e.GetPosition(listView));
            //var a = ((FrameworkElement)e.OriginalSource).DataContext;

        }
    }
}

Any idea how to fix this issue, I have gone through several websites but no answers were found.

trickymind
  • 557
  • 5
  • 21
  • Can you place a breakpoint in the top of GetPagedItemsAsync() and check the pageIndex and pageSize when you navigate back to Courses page? – Andrew KeepCoding Mar 31 '23 at 23:17
  • I tried adding a breakpoint, it will start calling the function with index 0 and calls it continuously until it reaches 394. GetPagedItemsAsync() is called 394 times. – trickymind Apr 01 '23 at 02:28
  • 1
    I created a sample app (no MySQL) but works as expected. This might be related to how you are using MySQL. I would 1) Try passing the ``CancellationToken`` from the GetPagedItemsAsync(), 2) [Caching the page](https://stackoverflow.com/a/75610812/2411960) for navigations and move the instatiation of ``IncrementalLoadingCollection`` to the constructor so it won't get called every navigations. – Andrew KeepCoding Apr 01 '23 at 04:14
  • Finally got it fixed just by adding `await Task.Delay(1, cancellationToken);` inside the function. Thank you for suggestion. – trickymind Apr 01 '23 at 12:00

1 Answers1

0

Finally got it fixed by adding await Task.Delay(1, cancellationToken); in the function. I'm not sure what was causing the issue but adding a small delay like 1ms fixed it.

The new code looks like:

public async Task<IEnumerable<Course>> GetPagedItemsAsync(int pageIndex, int pageSize, CancellationToken cancellationToken = default)
{
    using MySqlConnection connection = new(GlobalDatabaseConfiguration.Url);
    await connection.OpenAsync(cancellationToken);
    MySqlCommand command = connection.CreateCommand();
    command.CommandText = "SELECT * FROM courses ORDER BY id LIMIT @startIndex, @pageSize";
    command.Parameters.AddWithValue("@startIndex", pageIndex * pageSize);
    command.Parameters.AddWithValue("@pageSize", pageSize);
    using MySqlDataReader reader = (MySqlDataReader)await command.ExecuteReaderAsync(cancellationToken);
    while (await reader.ReadAsync(cancellationToken))
    {
        Course course = new()
        {
            Id = reader.GetInt32("id"),
            Name = reader.GetString("name"),
            Semesters = reader.GetInt32("semesters"),
            Timestamp = DateTime.Parse(reader.GetString("timestamp"))
        };
        courseList.Add(course);
    }

    //This line fixed it.
    await Task.Delay(1, cancellationToken);
    return (from course in courseList select course).Skip(pageIndex * pageSize).Take(pageSize);
}
trickymind
  • 557
  • 5
  • 21