How to load images to a page as soon as they are loaded from a folder in UWP

132 views Asked by At

I'm using the AdaptiveGridView to load a list of images from a folder onto the page. my question is how can i load an image as soon as it's become ready and not have to wait for the entire list of images to be processed. Here's my code: C#

 public class Images
        {
            public ImageSource ImageURL { get; set; }
            public string ImageText { get; set; }
        }

        private List<Images> ImageCollection;

        private async void Button_Click(object sender, RoutedEventArgs e)
        {
            ImageCollection = new List<Images>();
            // pick a folder
            var folderPicker = new Windows.Storage.Pickers.FolderPicker();
            folderPicker.FileTypeFilter.Add(".jpg");
            var folder = await folderPicker.PickSingleFolderAsync();
            var filesList = await folder.CreateFileQueryWithOptions(new QueryOptions(CommonFileQuery.DefaultQuery, new string[] { ".jpg", ".png", ".jpeg" })).GetFilesAsync();
            for (int i = 0; i < filesList.Count; i++)
            {
                StorageFile imagefile = filesList[i];
                BitmapImage bitmapimage = new BitmapImage();
                using (IRandomAccessStream stream = await imagefile.OpenAsync(FileAccessMode.Read))
                {
                    bitmapimage.SetSource(stream);
                }

                ImageCollection.Add(new Images()
                {
                    ImageURL = bitmapimage,
                    ImageText = filesList[i].Name
                });
                countPhotos.Text = i.ToString() + "out of" + filesList.Count.ToString();

            }
            AdaptiveGV.ItemsSource = ImageCollection;

        }

XAML:

<Page.Resources>
        <DataTemplate x:Key="PhotosList">
            <Grid>
                <Image Source="{Binding ImageURL}"
                       Stretch="UniformToFill"
                       HorizontalAlignment="Center"
                       VerticalAlignment="Center">

                </Image>
            </Grid>
        </DataTemplate>
    </Page.Resources>
    <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Button HorizontalAlignment="Left" VerticalAlignment="Top" Content="click me" Click="Button_Click">

        </Button>
        <TextBlock Name="countPhotos">

        </TextBlock>
        <ScrollViewer>

            <UWPToolkit:AdaptiveGridView x:Name="AdaptiveGV"
                                                 ItemHeight="300" DesiredWidth="500"
                                                 ItemTemplate="{StaticResource PhotosList}">

            </UWPToolkit:AdaptiveGridView>
        </ScrollViewer>



    </StackPanel>

I tired moving the AdaptiveGV.ItemsSource = ImageCollection; inside of the for loop but that slowed down the process and I dont think its the best way to accomplish what i'm trying to do here. any other suggestions? Thanks

1

There are 1 answers

1
Clemens On BEST ANSWER

You should use an ObservableCollection instead of a List. You could then assign the collection to the ItemsSource property before adding the images. The ObservableCollection notifies the UI about changes, e.g. about added elements.

Besides that, you should also use the async BitmapImage.SetSourceAsync() method.

public class ImageItem
{
    public ImageSource Image { get; set; }
    public string ImageText { get; set; }
}

private async void Button_Click(object sender, RoutedEventArgs e)
{
    var imageCollection = new ObservableCollection<ImageItem>();
    AdaptiveGV.ItemsSource = imageCollection;

    for ...
    {
        ...
        await bitmapimage.SetSourceAsync(stream); 

        ...
        imageCollection.Add(new ImageItem()
        {
            Image = bitmapimage,
            ImageText = filesList[i].Name
        });
    }
}

Note also that I've replaced the (confusing) names Images and ImageURL by ImageItem and Image. You would have to change the Image.Source Binding in the DataTemplate to this:

<Image Source="{Binding Image}"

The next step could be to create a view model that holds the ObservableCollection<ImageItem> as a (read-only) property:

public class ViewModel 
{
    public ObservableCollection<ImageItem> ImageCollection { get; }
        = new ObservableCollection<ImageItem>();

    public async Task LoadImages(StorageFolder folder)
    {
        var queryOptions = new QueryOptions(
            CommonFileQuery.DefaultQuery, new string[] { ".jpg", ".png", ".jpeg" });
        var files = await folder.CreateFileQueryWithOptions(queryOptions).GetFilesAsync();

        foreach (var file in files)
        {
            using (var stream = await file.OpenReadAsync())
            {
                BitmapImage image = new BitmapImage();
                await image.SetSourceAsync(stream);

                ImageCollection.Add(new ImageItem
                {
                    Image = image,
                    ImageText = file.Name
                });
            }
        }
    }
}

You would assign the Page's DataContext to an instance of the view model in the Page constructor and call the LoadImages() method on Button Click:

public MainPage()
{
    InitializeComponent();
    DataContext = new ViewModel();
}

private async void Button_Click(object sender, RoutedEventArgs e)
{
    var folderPicker = new Windows.Storage.Pickers.FolderPicker();
    folderPicker.FileTypeFilter.Add(".jpg");
    var folder = await folderPicker.PickSingleFolderAsync();

    if (folder != null)
    {
        await ((ViewModel)DataContext).LoadImages(folder);
    }
}

In XAML, you would bind the ItemsSource property to the ImageCollection property like this:

<UWPToolkit:AdaptiveGridView ...
    ItemsSource="{Binding ImageCollection}"
    ItemTemplate="{StaticResource PhotosList}" />