0
votes

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>

1
is _currentImagePath the absolute path for an image ? is it something like "D:\somefolder\someimage.jpg"? if yes, you would need to add the folder to future access list since UWP apps cannot directly access any folder which the user has not explicitly provided access to (except the ApplicationData of course).this answer might help you stackoverflow.com/questions/47344292/… - Pratyay
It seemed that your code is not complete. The Image is not the general type in UWP. Please upload a minimal reproducible example, then I could help you diagnose this issue. - Xie Steven
In the test that I've made, yes it is an absolute path has your mentioned. But it is not a necessity, has long has I can show an image in the image control. In fact I'll like to create my own picture frame app for my old WinRT tab and maybe put it on the Ms Store - Hugo
Don't use System.IO.File; use Windows.Storage. bitmap.SetSourceAsync(await storageFile.OpenAsync(FileAccessMode.Read)). - Raymond Chen

1 Answers

0
votes

Raymond is right!

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

A goody for Raymond!