Stack
Stack

Reputation: 348

View does not show ViewModel property changes after async operation

My ViewModel has a 30 second data refresh service delegate method:

public Task OnDataRefreshed(List<MyType> data)
{
    this.Data = data;
    LongRunningGetDetailsAsync();
    return Task.FromResult(0);
} 

Public property Data is displayed and refreshed in the view properly.

The intention here is not to await the async task (fire-and-forget) LongRunningGetDetailsAsync() as it will introduce a significant delay before the Data is displayed if executed sync. I want to show Data ASAP and then let async task fetch the details at its own pace and let the view binding catch up then.

private async Task LongRunningGetDetailsAsync()
{
    foreach (MyType dataitem in this.Data)
    {
        dataitem.Details = await _apiEndpointService.GetDetails(dataitem.Id);
    }
}

LongRunningGetDetailsAsync() is where the binding is not firing. I set a break point at the end of LongRunningGetDetailsAsync watching Data.Details - the Data.Details are there, but it is never displayed in the view.

Thank you in advance for your time!

EDIT: Changed to

public async Task OnDataRefreshed(ObservableCollection<MyType> data)
{
    this.Data = data;
    await LongRunningGetDetailsAsync();
} 

"Data":

    public class MyType
    {
        public string MyProperty { get; set; }
        public string Details { get; set; }
    }

    private ObservableCollection<MyType> _data;
    public ObservableCollection<MyType> Data
    {
        get { return _data; }
        set
        {
            if (SetProperty(ref _data, value))
            {
                RaisePropertyChanged(() => Data);
            }
        }
    }

View binding:

        <Mvx.MvxListView
        local:MvxBind="ItemsSource Data"
        local:MvxItemTemplate="@layout/listitem" 
        ... />

listitem:

       <TextView local:MvxBind="Text MyProperty" ...
       <TextView local:MvxBind="Text Details" ...

Upvotes: 0

Views: 652

Answers (2)

default
default

Reputation: 11635

Since you're changing the Details property you need to let the view know that it has changed. You would need to use the same logic as you do for your Data property:

private string _details;
public string Details
{
    get { return _details; }
    set
    {
        if (SetProperty(ref _details, value))
        {
            RaisePropertyChanged(() => Details);
        }
    }
}

As a side note, try to avoid setters for your lists. In the future you may have other logic connected to your lists which, when overwritten, just complicates things. Instead, since it is exposed as an ObservableCollection<T> you can call Clear and then Add on the value from the getter.

Upvotes: 0

Alireza Noori
Alireza Noori

Reputation: 15253

First, use await keyword when you're calling your function and mark your function as async.

public async Task OnDataRefreshed(List<MyType> data)
{
    this.Data = data;
    await LongRunningGetDetailsAsync();
}

Now as I understand, you want to show items in your UI as soon as you get each of them. If your bindings are correct, the changes above should do what you need. You should probably use ObservableCollection<> instead of List<> for Data property though.

Upvotes: 1

Related Questions