CollectionView API image loading

Eduardo Gomez 3,426 Reputation points
2023-11-13T14:14:06.5066667+00:00

Hello

I am calling an API, for getting some images

public async Task GetCardNamesAsync(string deckName) {
    CardList.Clear();

    IsBusy = true;

    var cards = await httpService.GetAsync<CardInfo>($"https://yugiohprices.com/api/set_data/{deckName}");


    if (cards != null) {

        foreach (var card in cards.data.cards) {

            var encodedName = Uri.EscapeDataString(card.name);

            var cardDetail = await httpService.GetAsync<CardDetail>(
                $"https://db.ygoprodeck.com/api/v7/cardinfo.php?name={encodedName}");

            card.id = cardDetail.Data.FirstOrDefault().Id;
            card.description = cardDetail.Data.FirstOrDefault().Desc;
            card.atk = cardDetail.Data.FirstOrDefault().Atk;
            card.def = cardDetail.Data.FirstOrDefault().Def;
            card.Race = cardDetail.Data.FirstOrDefault().Race;
            card.Attribute = cardDetail.Data.FirstOrDefault().Attribute;
            card.imageBytes = await httpService.GetByteArrayAsync(card.image_url);

            CardList.Add(card);
        }
    }

    IsBusy = false;


Foreach card name, I am getin the image in a bytes array. I have a a varible isBusy, to let the ue interface that is doing something

The problem is that is dowloading one by one

<CollectionView
    ItemsLayout="VerticalGrid, 4"
    ItemsSource="{Binding CardList}"
    SelectionMode="None">
    <CollectionView.EmptyView>
        <ActivityIndicator Color="Purple" IsRunning="{Binding IsBusy}" WidthRequest="80" HeightRequest="80"/>
    </CollectionView.EmptyView>
    <CollectionView.ItemTemplate>
        <DataTemplate x:DataType="model:Card">
            <Image
                Margin="10"
                Aspect="AspectFit"
                MinimumHeightRequest="100"
                MinimumWidthRequest="100"
                Source="{Binding imageBytes, Converter={StaticResource ArrayToImage}}">
                <Image.GestureRecognizers>
                    <TapGestureRecognizer
                        Command="{Binding Source={x:Reference cardsPage}, Path=BindingContext.SelectedCardCommand}"
                        CommandParameter="{Binding .}" />
                </Image.GestureRecognizers>
            </Image>
        </DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView>

What I want to do is to display my collectionView whe evrery card has been downloaded

this is the code for downloading my image


        try {

            using var res = await client.GetAsync(url);
            if (res.IsSuccessStatusCode) {
                using var stream = await res.Content.ReadAsStreamAsync();
                using var memoryStream = new MemoryStream();
                await stream.CopyToAsync(memoryStream);
                return memoryStream.ToArray();
            }
        } catch (Exception e) {
            Debug.WriteLine(e.Message);
        }

        return default;
    }
.NET MAUI
.NET MAUI
A Microsoft open-source framework for building native device applications spanning mobile, tablet, and desktop.
3,425 questions
0 comments No comments
{count} votes

Accepted answer
  1. Yonglun Liu (Shanghai Wicresoft Co,.Ltd.) 42,166 Reputation points Microsoft Vendor
    2023-11-14T08:08:26.73+00:00

    Hello,

    According to your code, this is causing slow loading issues due to the fact that all the data has to be re-downloaded every time you click on the deck.

    Here's how you could cache online images locally and avoid loading the same card multiple times.

    First, Add the following property into Card class.

    public string image_path { get; set; }
    

    Use the following method for caching when loading images.

    public async Task GetCardNamesAsync(string deckName)
    {
        CardList.Clear();
    
        IsBusy = true;
    
        var cards = await httpService.GetAsync<CardInfo>($"https://yugiohprices.com/api/set_data/{deckName}");
    
    
        if (cards != null)
        {
    
            foreach (var card in cards.data.cards)
            {
    
                var encodedName = Uri.EscapeDataString(card.name);
    
                var cardDetail = await httpService.GetAsync<CardDetail>(
                    $"https://db.ygoprodeck.com/api/v7/cardinfo.php?name={encodedName}");
    
                card.id = cardDetail.Data.FirstOrDefault().Id;
                card.description = cardDetail.Data.FirstOrDefault().Desc;
                card.atk = cardDetail.Data.FirstOrDefault().Atk;
                card.def = cardDetail.Data.FirstOrDefault().Def;
                card.Race = cardDetail.Data.FirstOrDefault().Race;
                card.Attribute = cardDetail.Data.FirstOrDefault().Attribute;
    
                var path = Path.Combine(FileSystem.CacheDirectory, card.id.ToString() + ".jpg");
                if (!File.Exists(path)) //Since cards are cached by ID, they will be directly changed to a local cache address when the same card is downloaded.
                {
                    var file = File.Create(path);
                    file.Write(await httpService.GetByteArrayAsync(card.image_url));
                }
    
                card.image_path = path;
                CardList.Add(card);
            }
        }
    
        IsBusy = false;
    }
    

    Finally, use a local cache address for loading.

    <Image
        Margin="10"
        Aspect="AspectFit"
        MinimumHeightRequest="100"
        MinimumWidthRequest="100"
        Source="{Binding image_path}">
    

    This is because your ActivityIndicator as an EmptyView. Therefore, when the first piece of data is added to the CardList, the EmptyView disappears, not because the isBusy property does not take effect.

    You can add a buffer list by adding data to the buffer list and then adding it to the CardList.

    public ObservableCollection<Card> TempCardList { get; } = new ObservableCollection<Card>();
    

    GetCardNamesAsync method:

    public async Task GetCardNamesAsync(string deckName)
    {
        CardList.Clear();
    
        IsBusy = true;
    
        var cards = await httpService.GetAsync<CardInfo>($"https://yugiohprices.com/api/set_data/{deckName}");
    
        if (cards != null)
        {
            foreach (Card card in cards.data.cards)
            {
                var encodedName = Uri.EscapeDataString(card.name);
    
                var cardDetail = await httpService.GetAsync<CardDetail>(
                    $"https://db.ygoprodeck.com/api/v7/cardinfo.php?name={encodedName}");
                card.id = cardDetail.Data.FirstOrDefault().Id;
                card.description = cardDetail.Data.FirstOrDefault().Desc;
                card.atk = cardDetail.Data.FirstOrDefault().Atk;
                card.def = cardDetail.Data.FirstOrDefault().Def;
                card.Race = cardDetail.Data.FirstOrDefault().Race;
                card.Attribute = cardDetail.Data.FirstOrDefault().Attribute;
    
                var path = Path.Combine(FileSystem.CacheDirectory, card.id.ToString() + ".jpg");
                if (!File.Exists(path))
                {
                    var file = File.Create(path);
                    file.Write(await httpService.GetByteArrayAsync(card.image_url));
                }
                card.image_path = path;
                TempCardList.Add(card);
            }
        }
        foreach (Card card in TempCardList)
        {
            CardList.Add(card);
        }
        IsBusy = false;
    }
    

    Best Regards,

    Alec Liu.


    If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.


0 additional answers

Sort by: Most helpful

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.