Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MVVM c# how to load async data into a property?

Tags:

c#

mvvm

wpf

I wonder if there's a better approach to load async data into a property. now I create an async function and raise a Task in the Get part of the property like this:

private ObservableCollection<CProyecto> prope;

public ObservableCollection<CProyecto> Prope
{
    get 
    {
        if (prope == null)
        {
            Task.Run(()=> LoadData()).Wait();
        }

        return proyectos;
    }
    set 
    { 
        prope = value; 
        RaisePropertyChanged(); 
    }
}

async private Task LoadData() 
{
    Prope = await clsStaticClassDataLoader.GetDataFromWebService();
}

This approach works, but I don't like the use of .Wait, because that can freeze the screen if the service doesn´t respond fast.

Can you please guide me on this matter?

thanks in advance

like image 971
KillemAll Avatar asked Apr 12 '17 11:04

KillemAll


2 Answers

I suggest you read my MSDN article on async MVVM data-binding. I have a library (github link) that provides a NotifyTask<T> type, which can be used as such:

public class MyClass : INotifyPropertyChanged
{
  public NotifyTask<ObservableCollection<CProyecto>> Prope { get; private set; }

  public MyClass()
  {
    // Synchronously *start* the operation.
    Prope = NotifyTask.Create(LoadDataAsync());
  }

  async private Task<ObservableCollection<CProyecto>> LoadDataAsync()
  {
    return await clsStaticClassDataLoader.GetDataFromWebService();
  }
}

Then your databinding would operate on Prope.Result.

The advantage of this approach is that you can also use databinding to hide/show busy indicators (Prope.IsNotCompleted), show controls when the data is available (Prope.IsSuccessfullyCompleted), and error notifications (Prope.IsFaulted / Prope.ErrorMessage).

Also, you can specify a non-null default value, if you wish:

Prope = NotifyTask.Create(LoadDataAsync(), new ObservableCollection<CProyecto>());
like image 140
Stephen Cleary Avatar answered Nov 18 '22 10:11

Stephen Cleary


The way I handled this was to start the process of loading the property when the object was constructed, but I did not await the result. Since the property notifies when it is populated, the bindings worked just fine. Essentially it works like this:

public class MyClass : INotifyPropertyChanged
{
    private ObservableCollection<CProyecto> prope;

    public ObservableCollection<CProyecto> Prope
    {
        get { return prope; }
        set { prope = value; RaisePropertyChanged(nameof(Prope)); }
    }

    public MyClass()
    {
        // Don't wait or await.  When it's ready
        // the UI will get notified.
        LoadData();
    }

    async private Task LoadData() 
    {
        Prope = await clsStaticClassDataLoader.GetDataFromWebService();
    }
}

This works very well, and does not cause any delays or stuttering in the UI. If you want the collection to never be null (a good practice IMO), you can pre-initialize the prope field with an empty collection.

like image 40
Berin Loritsch Avatar answered Nov 18 '22 12:11

Berin Loritsch