Apologies if this question sounds a bit generic. I’m just looking for some general advice about how to go about solving this problem 🙂
Right now I have the following async method which is retrieving a list of files in a custom FileService. This is being fired in the Constructor of a ViewModel.
private async void Construct()
{
Files = new ObservableCollection<FileViewModel>();
IList _files = await _fileRepository.GetFiles();
foreach (File file in _files)
{
Files.Add(new FileViewModel(file));
}
}
As you can probably work out, the async method is retrieving the list of files in 1 step, and then adding them to list (which is the binding source in the UI) to another.
To the user, they will see nothing until the async process has finished, and then after a time they will see all the results at once.
In my head, this defeats the point of async as what I would really want is the results being added as they are being retrieved.
To add another step, I also have a linq query which pulls the first letter of each file to group in to categories. This executes after the foreach loop. How would I go about adding this step also?
Thank you very much for any adivce you can give!
You’ll have to change your repository at a fundamental level in order to do this. You could return an object which fires events when the items are retrieved and then in the event handler add your object to an
INotifyCollectionChangedinterface implementation which your control is bound to.You could also look into the Reactive Extensions and expose an
IObservable<T>interface implementation which when subscribed to, would add the item to the data source (again, implementingINotifyCollectionChanged).Regardless of which approach you take, you have to take into account the fact that UI components an only be updated on the thread that they are created on, meaning you have to marshal the call to the UI thread.
The easiest way to do this to capture the
SynchronizationContextusing the staticCurrentproperty before you make any asynchronous calls and then call thePostmethod with the call to add the item to theINotifyCollectionChangedimplementation.async/awaitdoesn’t really lend itself to use in this situation; it’s not well-suited for notifications when an operation is complete but to weave continuations on asynchronous operations when they’re complete.You might want to reconsider these approaches though. With most LINQ providers, an
IQueryable<T>is returned, which derives fromIEnumerable<T>. While there certainly can be blocking while theIEnumerable<T>is iterated through, most providers stream the results in rather quick succession, chances are too quick to give you the effect that you want.If the LINQ provider is providing them too slowly (large gaps between each iteration), then you can use the
ToObservableextension method in the Reactive Framework to enable one of the patterns mentioned above to give you the items to place in your list as soon as you get them.