0
votes

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();
} 
  • still have the same issue. Data is bound to Mvx.MvxListView. If the list is long and an item happens to be out of view, once scrolled to, it displays the updated model OK.

"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" ...
2
Are you using the INotifyPropertyChanged interface? How does the property Details look like? - Default
@Default Details is primitive - please see the edit above - Stack
You need to fire PropertyChanged when it changes. Could you also add how you bind it in your view? - Default
@Default RaisePropertyChanged(() => Data); on set, View - please see the addition above - Stack
yes, well, you need it on the Details property - that is the one changing. Your Data doesn't (in the call Details = ..). Also, try to avoid setters for your collections. If you need to add new data then use Clear and Add[Range] instead. - Default

2 Answers

1
votes

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.

0
votes

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.