How to load data in DataGrid Asynchronously

There might be a time when you came across a situation when you are trying to load data in DataGrid in your WPF UI and UI gets Hanged (stuck) while the data is being loaded.

You need to avoid this situation and make the data loading in the background in a way that your Application UI is not frozen/hanged while the data loading is in progress.

You might have the ViewModel like this:

public class ShellViewModel
{
  private ObservableCollection<Customer> _customers;
  
  public ObservableCollection<Customer> Customers
  {
    get { return _customers; }
    set { _customers = value; }
  }
  public ShellViewModel()
  {
    _customers = new ObservableCollection<Customer>();
  }

  public void LoadData()
  {
    //GetCustomers reading from database and returns the list
    foreach (Customer c in DataHelper.GetCustomers())
    {
      _customers.Add(c);
    }    
  }
}

So, what happens here is while LoadData() is adding customers to the _customers collection, the UI stays stuck and doesn’t refresh and it appears to be hanged and doesn’t allow any operation.

You need to implement the asynchronous behavior in this so that the data loading continues in the backend, and UI stays active for user interaction.

One can think of just implementing the _customers.Add(c); in a separate Thread or Task, but it does not solve the problem as _customers collection will get all the customers in it, but the UI will not show it UI is not refreshed.

So, the solution is very simple and are as follows:

public class ShellViewModel
{
  private ObservableCollection<Customer> _customers;
  private object _lockMutex = new object();

  public ObservableCollection<Customer> Customers
  {
    get { return _customers; }
    set { _customers = value; }
  }

  public ShellViewModel()
  {
    _customers = new ObservableCollection<Customer>();

    //Just ask the binding to enable synching of the collection
    BindingOperations.EnableCollectionSynchronization(Customers, _lockMutex);
  }

  public void LoadData()
  {
    _ = LoadDataAsync();
  }

  private Task LoadDataAsync()
  {
    return Task.Factory.StartNew(() =>
           {
             //GetCustomers reading from database and returns the list
             foreach (Customer c in DataHelper.GetCustomers())
             {
               _customers.Add(c);
             }
           });
  }
}

Here, you are just making the Loading of data in an Async method (using Task Library) which makes the loading actually background/async.

And make a slight change in the constructor BindingOperations.EnableCollectionSynchronization(Customers, _lockMutex); to ask the WPF binding to enable synchronization of the binding collection across multiple threads (data loading thread and UI thread here), so that whenever there is any change/addition in the binding collection, UI is automatically refreshed.

Read more about BindingOperations.EnableCollectionSynchronization().

Using Caliburn.Micro

Caliburn.Micro is a nice and lightweight framework for your WPF application as it provides a very easy and robust way to implement and adhere to the MVVM pattern in your application.

When you are using the Caliburn.Micro MVVM framework for your WPF application, you can simply use BindableCollections to achieve the same result without even bothering about explicitly writing for synchronization.