Xamarin - асинхронное связывание данных - PullRequest
0 голосов
/ 22 марта 2020

У меня следующий код:

Страница с множеством изображений, которые загружаются динамически с привязкой данных:

 base.OnAppearing();
        if (!loaded)
        {
            loaded = true;

            BindingContext = new GalleryViewModel(pCode, gCode, gUrl);

        }

viewmodel:

namespace GalShare.ViewModel
{
class GalleryViewModel
{
    public string pCode { get; set; }
    public string gCode { get; set; }
    public string gUrl { get; set; }
    public ObservableCollection<picdata> Galleries { get; set; }          
    public  GalleryViewModel(string pCode, string gCode, string gUrl)
    {
        this.pCode = pCode;
        this.gCode = gCode;
        this.gUrl = gUrl;
        Galleries = new GalleryService().GetImageList(pCode,gCode,gUrl);

    }

}
}

galleryservice. cs

   class GalleryService
 {

    public ObservableCollection<picdata> Images { get; set; }
    public ObservableCollection<picdata> GetImageList(string pCode, string gCode, string gUrl)
    {
        WebClient client = new WebClient();
        Images = new ObservableCollection<picdata>();
        string downloadString = client.DownloadString(gUrl);
        var deserialized = JsonConvert.DeserializeObject<JsonTxt>(downloadString);

            foreach (File img in deserialized.Files)
            {
               Images.Add(new picdata()
               {
                    ImageName = img.file,
                    BaseUrl = deserialized.Settings.Path.ToString(),
                    ThumbUrl = deserialized.Settings.Path.ToString() + "/thumbs" + img.file
               });
            }
        return Images;
    }
}

XAML страницы:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
         xmlns:vm="clr-namespace:GalShare.ViewModel"
         xmlns:d="http://xamarin.com/schemas/2014/forms/design"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
         xmlns:ffimageloading="clr-namespace:FFImageLoading.Forms;assembly=FFImageLoading.Forms"
         mc:Ignorable="d"
         x:Class="GalShare.Views.Gallery">

<StackLayout>
    <CollectionView ItemsSource="{Binding Galleries}" x:Name="myCollection" SelectionMode="Single" SelectionChanged="CollectionView_SelectionChanged">
        <CollectionView.ItemsLayout>
            <GridItemsLayout Orientation="Vertical"
                    Span="2" />
        </CollectionView.ItemsLayout>
        <CollectionView.ItemTemplate>
            <DataTemplate>
                <ffimageloading:CachedImage Source="{Binding ThumbUrl}" CacheDuration="1" HorizontalOptions="Fill" VerticalOptions="Fill" DownsampleToViewSize="False"></ffimageloading:CachedImage>
            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>
</StackLayout>

</ContentPage>

Код работает, но, учитывая, что изображения загружаются из Интернета, пока я загружаю данные, приложение заблокировано. Как это можно сделать асинхронно? Я хотел бы загрузить страницу назначения, а затем загрузить содержимое, пока я там. В настоящее время приложение останавливается на странице, которая загружает эту страницу, пока все не будет загружено.

Я пытался выполнить задачи / жду но безуспешно Я думаю, что мне нужно переместить некоторые вещи, чтобы выполнить код асинхронно, но не могу понять, как.

1 Ответ

2 голосов
/ 23 марта 2020

Вы отметили async-await и пишете асинхронно в своем заголовке. Однако весь ваш код выполняется в основном потоке, а не асинхронно.

Вместо загрузки данных в конструктор ViewModel. Я настоятельно рекомендую вам использовать событие жизненного цикла, такое как OnAppearing на вашем Page и запускать ICommand для асинхронной загрузки ваших данных.

Кроме того, я бы переключился на использование HttpClient и его приятной асинхронности c API. Так что-то вроде:

public class GalleryService
{
    private HttpClient _httpClient;

    public GalleryService()
    {
        _httpClient = new HttpClient();
    }

    public async Task<IEnumerable<picdata>> GetImageList(string pCode, string gCode, string gUrl)
    {
        var response = await _httpClient.GetAsync(gUrl).ConfigureAwait(false);
        if (response.IsSuccessStatusCode)
        {
            var json = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
            var deserialized = JsonConvert.DeserializeObject<JsonTxt>(json);

            var images = new List<picdata>();
            foreach(var img in deserialized.Files)
            {
                images.Add(new picdata()
                {
                    ImageName = img.file,
                    BaseUrl = deserialized.Settings.Path.ToString(),
                    ThumbUrl = deserialized.Settings.Path.ToString() + "/thumbs" + img.file
                });
            }

            return images;
        }

        return new picdata[0]; // return empty set
    }
}

и ViewModel:

public class GalleryViewModel
{
    private GalleryService _galleryService;

    public ObservableCollection<picdata> Galleries { get; } = new ObservableCollection<picdata>();
    public ICommand GetImagesCommand { get; }

    public GalleryViewModel(string pCode, string gCode, string gUrl)
    {
        _galleryService = new GalleryService();

        GetImagesCommand = new Command(async () => DoGetImagesCommand(pCode, gCode, gUrl));
    }

    private async Task DoGetImagesCommand(string pCode, string gCode, string gUrl)
    {
        var images = await _galleryService.GetImageList(pCode, gCode, gUrl);
        foreach(var image in images)
            Galleries.Add(image);
    }
}

Затем в вашем OnAppearing() переопределении на вашей странице вы можете вызвать что-то вроде: (BindingContext as GalleryViewModel).GetImagesCommand.Execute(null);

Убедитесь, что вы установили BindingContext, прежде чем пытаться вызвать команду. Это можно сделать в конструкторе страницы с помощью:

BindingContext = new GalleryViewModel();

Таким образом, вы не блокируете весь свой пользовательский интерфейс, пока не закончится загрузка изображений. В качестве альтернативы вы можете запустить задачу с помощью Task.Run в конструкторе ViewModel. Однако тогда вам придется маршалировать совокупность ObservableCollection в потоке пользовательского интерфейса.

...