Display images in UWA that is not from App Assets

0

I've some hard time trying to display images in my UWP App. My images are located in the Images library.

The question is: How to show jpg in the Image Xaml control on a UWP, that is not in the projects "Assets"?

So far using Binding, I’ve

  • Try to set Image.Source to the full path with no success, image control remains blank. (for file access retriction a guess)

  • Try using setting BitMapImage to the Image.Source property with no success, image control remains blank. (for file access retriction a guess)

  • Try to assign FileStorage class to the Image.Source property with no success, image control remains blank.

  • Try to load the image file in a stream and load the stream in the BitmapImage, but there I’ve threading issues. More about this below.

About the threading issue, I’ve come to two issues for now. When loading the file/stream:

var file = File.ReadAllBytes(_currentImagePath);

I get

System.InvalidOperationException: 'Synchronous operations should not be performed on the UI thread. Consider wrapping this method in Task.Run.'

When using Task.Run on any manner I get an exception when trying to set the Bitmap source

The application called an interface that was marshalled for a different thread. (Exception from HRESULT: 0x8001010E (RPC_E_WRONG_THREAD))

var bitmap = new BitmapImage();
return await Task.Run(async () =>
{
   var file = File.ReadAllBytes(_currentImagePath);
   using (var stream = new InMemoryRandomAccessStream())
   {
       await stream.WriteAsync(file.AsBuffer());
       stream.Seek(0);
       await bitmap.SetSourceAsync(stream); // **Exception here**
       return bitmap;
    }
});

Here the full source code:

namespace App1.Images
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Diagnostics;
    using System.IO;
    using System.Linq;
    using System.Runtime.CompilerServices;
    using System.Runtime.InteropServices.WindowsRuntime;
    using System.Threading.Tasks;
    using Windows.Storage;
    using Windows.Storage.Streams;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Media.Imaging;
    using App1.Annotations;


    public class ImagesLibrairies : INotifyPropertyChanged
    {
        private readonly HashSet<Image> _images;
        private readonly string _fileTypes;
        private readonly HashSet<Image> _preferrednessFiles;
        private readonly DispatcherTimer _timer;

        private ulong _minFileSize = 5 * 1024; // 5k in bytes
        private ulong _maxFileSize = 1024 * 1024 * 20; //20 MBytes 
        private int _imageIndex = 0;

        public ImagesLibrairies()
        {
            _preferrednessFiles = new HashSet<Image>();
            _fileTypes = "*.jpg;*.jpeg;*.bmp;*.tif"; // ignore *.ico;*.png;*.svg;*.gif
            _images = new HashSet<Image>();

            _timer = new DispatcherTimer { Interval = new TimeSpan(0, 0, 0, 5) };
            _timer.Tick += _timer_Tick;
        }

        private Uri _currentImage;
        public Uri CurrentImage
        {
            get => _currentImage;
            private set
            {
                _currentImage = value;
                OnPropertyChanged(nameof(CurrentImage));
                OnPropertyChanged(nameof(Image));
            }
        }

        public async Task<BitmapImage> GetBitmapImage()
        {

            var bitmap = new BitmapImage();
            return await Task.Run(async () =>
              {
                  var file = File.ReadAllBytes(_currentImagePath);
                  using (var stream = new InMemoryRandomAccessStream())
                  {
                      await stream.WriteAsync(file.AsBuffer());
                      stream.Seek(0);
                      await bitmap.SetSourceAsync(stream);
                      return bitmap;
                  }
              });

        }

        public BitmapImage Image
        {
            get
            {
                if (!string.IsNullOrEmpty(_currentImagePath))
                    return GetBitmapImage().Result;

                return null;

            }
        }

        private string _currentImagePath;
        public string CurrentImagePath
        {
            get => _currentImagePath;
            set
            {
                _currentImagePath = value;
                OnPropertyChanged(nameof(CurrentImagePath));

                CurrentImage = new Uri(value);
            }
        }

        public object CurrentStorageFile { get; set; }

        private void _timer_Tick(object sender, object e)
        {
            SetNext();
        }

        public async Task ScanLibrairies()
        {

            var picturesFolder = KnownFolders.PicturesLibrary;

            var files = await picturesFolder.GetFilesAsync();

            foreach (var file in files)
            {

                Debug.WriteLine(file.Name + ", " + file.DateCreated);

                if (_images.Count >= int.MaxValue)
                    return;

                var prop = await file.GetBasicPropertiesAsync();

                var imageLocation = new Image
                {
                    StorageFile = file,
                    Properties = prop
                };

                if (imageLocation.Properties.Size < _minFileSize || imageLocation.Properties.Size > _maxFileSize)
                    continue;

                if (_preferrednessFiles.Any(o => o.Equals(imageLocation) && o.Preferredness == Preferredness.No))
                    continue;

                _images.Add(imageLocation);

            }
        }

        public void SetNext()
        {
            if (_images == null || !_images.Any())
            {
                return;
            }

            _imageIndex++;

            var image = _images.Skip(_imageIndex).FirstOrDefault();
            if (image != null)
            {
                Debug.WriteLine($"Displaying: {image.StorageFile.Path}");
            }

            CurrentImagePath = image?.StorageFile.Path;
            CurrentImage = new Uri(CurrentImagePath);
        }

        public void SetPrevious()
        {
            if (_images == null || !_images.Any())
                return;

            _imageIndex--;
            var image = _images.Skip(_imageIndex).FirstOrDefault();
            CurrentImagePath = image?.StorageFile.Path;


        }

        public void LikeThisPicture(Image picture)
        {
            var pic = _preferrednessFiles.FirstOrDefault(o => o.Equals(picture));
            if (pic == null)
            {
                picture.Preferredness = Preferredness.Very;
                _preferrednessFiles.Add(picture);
                return;
            }

            pic.Preferredness = Preferredness.Very;
        }

        public void DislikeThisPicture(Image picture)
        {
            var pic = _preferrednessFiles.FirstOrDefault(o => o.Equals(picture));
            if (pic == null)
            {
                picture.Preferredness = Preferredness.No;
                _preferrednessFiles.Add(picture);
                return;
            }

            pic.Preferredness = Preferredness.No;
        }

        public void StartAsync()
        {
            ScanLibrairies();
            _timer.Start();

        }

        public event PropertyChangedEventHandler PropertyChanged;

        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Here the xaml markup

<Page.DataContext>
    <images:ImagesLibrairies/>
</Page.DataContext>

<Grid >
    <!--Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">-->

    <Image Stretch="UniformToFill"  Margin="15">
        <Image.Source>
            <BitmapImage UriSource="{Binding Image}"></BitmapImage>
            <!--<BitmapImage UriSource="{Binding CurrentImagePath}"></BitmapImage>-->
            <!--<BitmapImage UriSource="D:\Utilisateurs\hugod\OneDrive\Images\jouets\20180106_132041.jpg"></BitmapImage>-->
            <!--<BitmapImage UriSource="Assets/Black-And-White-Wall-Brick-Texture-Shadow-WallpapersByte-com-3840x2160.jpg"></BitmapImage>-->
            <!--<BitmapImage UriSource="{Binding CurrentStorageFile}"></BitmapImage>-->
        </Image.Source>
    </Image>


</Grid>

c#
image
uwp
visual-studio-2017
asked on Stack Overflow Apr 5, 2018 by Hugo • edited Apr 6, 2018 by Hugo

1 Answer

0

Raymond is right!

CurrentImage = new BitmapImage();
await CurrentImage.SetSourceAsync(await image.StorageFile.OpenAsync(FileAccessMode.Read));

A goody for Raymond!

answered on Stack Overflow Apr 8, 2018 by Hugo

User contributions licensed under CC BY-SA 3.0