0

What is best practice for doing this? Keep in mind that I am using the MVVM pattern and Windows Template studio. So if there is some built in solution/best practice for dataloading I would apprecihate to know about that.

I am loading data async from an API through http which takes a couple of seconds. So when a user clicks on the page he have to wait a bit before the data appears. I want the data to be preloaded before the user clicks the page. The page data should be loaded when application starts.

This is my code for loading data into the page right now:

XAML

<i:Interaction.Behaviors>
    <ic:EventTriggerBehavior EventName="Loaded">
        <ic:InvokeCommandAction Command="{x:Bind ViewModel.LoadedCommand}" />
    </ic:EventTriggerBehavior>
</i:Interaction.Behaviors>

ViewModel:

public ICommand LoadedCommand => loadedCommand ?? (loadedCommand = new RelayCommand(LoadData));

private async void LoadData()
    {
        await LoadItemsAsync();
        await LoadImagesAsync();
    }

    private async Task LoadItemsAsync()
    {
        var items = await itemsDataAccess.GetAllAsync();

        foreach (Item i in items)
            ItemImageLinks.Add(new ItemImageLink() { Item = i });
    }

    private async Task LoadImagesAsync()
    {
        if (!InternetConnectionService.IsConnected())
            return;

        foreach (ItemImageLink iml in ItemImageLinks)
            iml.Image = await imagesDataAccess.GetImageAsync(iml.Item.ImageStringName);
    }
andersbs
  • 187
  • 1
  • 11

1 Answers1

0

You could use a combination of the Lazy and Task classes.

Declaration of the field:

private Lazy<Task<ItemImageLink[]>> ItemImageLinks;

Initialization in the constructor:

ItemImageLinks = new Lazy<Task<ItemImageLink[]>>(() => Task.Run(async () =>
{
    return await itemsDataAccess.GetAllAsync()
        .Select(item => new ItemImageLink() { Item = item })
        .ToArray();
}));

Preloading in a PreLoad method:

await ItemImageLinks.Value;

Usage anywhere is neaded:

foreach (ItemImageLink item in await ItemImageLinks.Value)
{
    // Do something with the item
}

Update: The Lazy wrapper is only needed if you must do the preloading at some arbitrary point in time. If you always preload immediately in the constructor then a simple Task<ItemImageLink[]> should be enough.

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
  • In retrospect I am no longer feeling comfortable with the `Lazy>` combination. It is a dangerous mix of a blocking and an asynchronous component, that may cause threads to be blocked and loss of scalability in some scenarios. Currently I am in favor of implementing the same functionality using nested tasks (`Task`), as shown [here](https://stackoverflow.com/questions/28340177/enforce-an-async-method-to-be-called-once/65714904#65714904 "Enforce an async method to be called once"). – Theodor Zoulias Jun 10 '22 at 18:52