Xamarin - Asynchronous data binding

99封情书 提交于 2020-03-25 13:42:19

问题


I have following code:

Page with lots of images, that are loaded dynamically with the databinding:

 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 of the page:

<?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>

The code works, but given that the images are loaded from the web, while I am loading the data the App is locked. How can this be done asynchronously? I'd like to load the destination page, and then load the content while I'm there.. Currently the app freezes on the page that loads this one until all is loaded.

I have tried with tasks/await but with no success. I think i have to move some things around to run the code asynchronously but cannot figure out how.


回答1:


You've tagged async-await and writting asynchronous in your title. However, all of your code is running on the main thread and not asynchronously.

Instead of loading your data in the constructor of the ViewModel. I highly suggest you use a lifecycle event such as OnAppearing on your Page and fire a ICommand to load your data asynchronously.

Additionally I would switch to using HttpClient and its nice async APIs. So something like:

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
    }
}

and 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);
    }
}

Then in your OnAppearing() override on your page you can call something like: (BindingContext as GalleryViewModel).GetImagesCommand.Execute(null);

Make sure you set your BindingContext before trying to call the command. This can be done in the Page constructor with:

BindingContext = new GalleryViewModel();

This way you are not blocking your entire UI until it is done downloading the images. Alternatively you could fire off a Task with Task.Run in the constructor of the ViewModel. However, then you will have to marshal the population of the ObservableCollection to the UI thread.



来源:https://stackoverflow.com/questions/60804577/xamarin-asynchronous-data-binding

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!