0

This is quite vexing. I am working on an app for image management. Part of the value is the ability to store images in sub-folders based on image properties, eg. creation date.

If I store the image source in a shallow folder (app\images\img.jpg), everything works fine. If I store the image in KnownFolders.Pictures\source\year\month\day\img.jpg, Image does not render. (Yes, that specific path won't work, I am trying to give you a sense of how the path is constructed)...

The file is actually there. The path is correct (I can open it in a browser, e.g.). The app has access to the file. But it does not render the bitmap.

I tried to render the bitmap manually using

new BitmapImage(new Uri("KnownFolders.Pictures\source\year\month\day\img.jpg"),UriKind.Absolute))

That does not render anything. (Again, assume the path is valid and has a file at its bottom).

What Am I Missing?

The head scratcher: for GIF anims, I am using Thomas Levesque's useful component: https://github.com/XamlAnimatedGif. That one, unfortunately, does only render gifs... and it does so even when the path is the one given above. So the Standard IMAGE control does not render correctly, but Thomas's control does... infuriating.

J. H.
  • 109
  • 9
  • Probably just an artifact of typing up your questions, but I'd expect to see the backslashes doubled ("source\\year"), or the string preceded with an @ (@"source\year") Just want to rule that out so as not to distract from the real issue. – Jeremy Hutchinson Mar 01 '17 at 18:43
  • `KnownFolders.Pictures` is not a valid part of a path name string. Besides that, an UWP app can't load image files from absolute URIs in the local file system. See [File access permissions](https://learn.microsoft.com/en-us/windows/uwp/files/file-access-permissions). – Clemens Mar 01 '17 at 19:27
  • As I said, never mind the path structures, they are illustrative. Paths are correct. That is not the issue. Oh, and yes, absolute paths are ok as long as they are in the StorageItemAccessList. Otherwise, the short paths would not work, wouldn' they... – J. H. Mar 01 '17 at 19:37

3 Answers3

0

An UWP app can't load a BitmapImage from an absolute URL to a file in a folder structure below the Pictures Library Folder.

So this won't work:

var relativePath = @"source\year\month\day\img.jpg";
var imageFile = await KnownFolders.PicturesLibrary.GetFileAsync(relativePath);
var bitmapImage = new BitmapImage(new Uri(imageFile.Path));

However, you could do this:

var relativePath= @"source\year\month\day\img.jpg";
var imageFile = await KnownFolders.PicturesLibrary.GetFileAsync(relativePath);
var bitmapImage = new BitmapImage();

using (var stream = await imageFile.OpenAsync(FileAccessMode.Read))
{
    await bitmapImage.SetSourceAsync(stream);
}
Clemens
  • 123,504
  • 12
  • 155
  • 268
  • Ok, I am rather confused. Because I did just that: load a bitmap from an absolute path... as the result of a databinding... that is weird... Just in case, to prevent confusion: loaded a Entity Framework entity with an absolute path. Made that path property the source of the IMAGE.source property. Worked just fine. Works just fine with absolute paths inside the APP folder... just not with an absolute path that has a few segments / hops inside... Ideas? – J. H. Mar 01 '17 at 20:23
  • Too late now, but a task for tomorrow: how would I use the SOURCE property of IMAGE in data binding (aka a string) to tie to something relative to the KnownFolder? I could parse/replace the absolute portion with something relative... what would that be. A GUID? – J. H. Mar 01 '17 at 20:31
  • Just to clarify why this is important: user may chose to place images into a different folder than KnownFolders.Pictures... She chooses that location with a FolderPicker. App remembers that choice and can access that location from now on... with an ABSOLUTE PATH (how else)... – J. H. Mar 01 '17 at 20:39
  • That won't work. The app must keep the StorageFile object. – Clemens Mar 01 '17 at 20:42
  • how do you "keep the StorageFile object" in persistence, aka SQLite? Oh wait, is that what StorageItemAccessList.GetFolderAsync is supposed to do? – J. H. Mar 01 '17 at 21:01
-1

So, after way too much time spent on this...

First, link to DataContextChanged of the IMAGE element. In there, parse the DataContext out. If you are using the IMAGE outside of an ItemsControl etc, this is not required...

private async void ImageView_DataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args)
{
    if (sender is Image)
    {
        Image img = (Image)sender;

        if (img.DataContext is ImageView)
        {
            MyViewDataContext dc = (MyViewDataContext)img.DataContext;

            img.Source = await dc.bitmap();
        }
    }
}

And here the implementation of MyViewDataContext.bitmap() which has a property called source that yields, you guessed it, absolute paths:

public async Task<BitmapImage> MyViewDataContext.bitmap()
        {
            if (_bitmap == null)
            {
                try
                {
                    StorageFile file = await StorageFile.GetFileFromPathAsync(source);
                    bool r = Windows.Storage.AccessCache.StorageApplicationPermissions.FutureAccessList.CheckAccess(file);
                    if (r)
                    {
                        using (IRandomAccessStream fileStream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read))
                        {
                            // create a new bitmap, coz the old one must be done for...
                            _bitmap = new BitmapImage();

                            // And get that bitmap sucked in from stream.
                            await _bitmap.SetSourceAsync(fileStream);
                        }
                    }
                }
                catch (Exception e)
                {
                    _bitmap = null;
                }
            }
            return _bitmap;
        }
        BitmapImage _bitmap;

I cache the resulting bitmap until I dispose of this MyViewDataContext.

I am now most concerned about memory. This one worries me: How to dispose BitmapImage cache?

So, as a tech debt, I am going to address the potential mem leaks later, once this whole thing is on the test bench and I can take a look at its runtime behavior...

Community
  • 1
  • 1
J. H.
  • 109
  • 9
-1

To access the folders and libraries represented by the properties of this class, specify the corresponding capabilities in your app manifest. For example, to access KnownFolders.PicturesLibrary, specify the Pictures Library capability in the app manifest.

Hope this will help KnowFolders

Jitendra.Suthar
  • 110
  • 2
  • 4
  • Hi Jitendra, of course that is already in the manifest. It appears that IMAGE does not deal with deeper paths correctly. The solution I posted below seems to work, but it leaks memory. So I am plugging a cache between IMAGE and the bitmap to prevent that. – J. H. Mar 04 '17 at 11:42