4

Is it possible to bind the image present in the Isolates storage to image control through xaml. I found some implementations like getting the image through the property and binding that into xaml control. But this is not the implementation what I am searching for. My question is like, writing an attach property and helper method to fetch the content from Isolated storage. I found a similar implementation in LowProfileImage class, used in windows phone 7. But I think it is deprecated now. If anyone tried similar implementations please help me to achieve the same. Also if implementation have any performance drains please mention that info too.

BenMorel
  • 34,448
  • 50
  • 182
  • 322
Stephan Ronald
  • 1,395
  • 2
  • 14
  • 39

2 Answers2

10

Yes, it is possible to use images from isolated storage in the app UI. It requires loading the image from the file into the BitmapImage and then binding ImageSource of your control to that BitmapImage. I'm using the following approach:

First, there's a method to load image asynchronously:

private Task<Stream> LoadImageAsync(string filename)
    {
        return Task.Factory.StartNew<Stream>(() =>
        {
            if (filename == null)
            {
                throw new ArgumentException("one of parameters is null");
            }

            Stream stream = null;

            using (var isoStore = IsolatedStorageFile.GetUserStoreForApplication())
            {
                if (isoStore.FileExists(filename))
                {
                    stream = isoStore.OpenFile(filename, System.IO.FileMode.Open, FileAccess.Read);                               
                }
            }
            return stream;
        });
    }

Then it can be used like this:

public async Task<BitmapSource> FetchImage()
    {
        BitmapImage image = null;
        using (var imageStream = await LoadImageAsync(doc.ImagePath))
        {
            if (imageStream != null)
            {
                image = new BitmapImage();
                image.SetSource(imageStream);
            }
        }
        return image;
    }

And finally you just assign return value of FetchImage() method to some of your view model's property, to which the UI element is bound. Of course, your view model should properly implement INotifyPropertyChanged interface for this approach to work reliably.


If you want to use attached properties approach, here's how you do it:

public class IsoStoreImageSource : DependencyObject
{
    public static void SetIsoStoreFileName(UIElement element, string value)
    {
        element.SetValue(IsoStoreFileNameProperty, value);
    }
    public static string GetIsoStoreFileName(UIElement element)
    {
        return (string)element.GetValue(IsoStoreFileNameProperty);
    }

    // Using a DependencyProperty as the backing store for IsoStoreFileName.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty IsoStoreFileNameProperty =
        DependencyProperty.RegisterAttached("IsoStoreFileName", typeof(string), typeof(IsoStoreImageSource), new PropertyMetadata("", Changed));

    private static void Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        Image img = d as Image;

        if (img != null)
        {
            var path = e.NewValue as string;
            SynchronizationContext uiThread = SynchronizationContext.Current;

            Task.Factory.StartNew(() =>
            {
                using (var isoStore = IsolatedStorageFile.GetUserStoreForApplication())
                {
                    if (isoStore.FileExists(path))
                    {
                        var stream = isoStore.OpenFile(path, System.IO.FileMode.Open, FileAccess.Read);
                        uiThread.Post(_ =>
                        {
                            var _img = new BitmapImage();
                            _img.SetSource(stream);
                            img.Source = _img;
                        }, null);
                    }
                }
            });               
        }
    }
}

And then in XAML:

<Image local:IsoStoreImageSource.IsoStoreFileName="{Binding Path}" />

Some limitations of this approach:

  • It only works on Image control, though you can change this to a whichever type you want. It's just not very generic.
  • Performance-wise, it will use a thread from the threadpool every time image source is changed. It's the only way to do asynchronous read from isolated storage on Windows Phone 8 right now. And you definitely don't want to do this synchronously.

But it has one one important advantage:

  • It works! :)
Haspemulator
  • 11,050
  • 9
  • 49
  • 76
  • +1 one for your effort. I am trying for something, that i can bind the isolated image path directly to image control with the help of a attached property. – Stephan Ronald Apr 19 '13 at 11:28
  • Okay, I see. I think it's possible, but would require some hacks. For example, you can only bind to a property, and properties cannot be asynchronous, like methods. And of course you cannot load image synchronously from the getter, it will kill your UI's performance. Maybe `Task.ContinueWith` and closures would help here. I'll try put a code sample for you quickly. – Haspemulator Apr 19 '13 at 11:37
  • Haspemulator, Can i ask one more doubt, what is the difference b/w "Deloyment.Current.Dispatcher.BeginInvole() && uiThread.Post() – Stephan Ronald Apr 19 '13 at 13:39
  • 3
    It will work with `Deployment.Current.Dispatcher.BeginInvoke()` too. The main difference is code portability. `System.Threading.SynchronizationContext` is a common concept for all .NET environments. This means, it will work equally well on Windows Store and Windows Phone 8 apps, while `Deployment.Current.Dispatcher` is only available on Windows Phone. Of course, `IsolatedStorageFile` is also Windows Phone specific, so in this particular case the difference between these two approaches is not that significant. But anyway, it's useful to keep the difference in mind, just in case. – Haspemulator Apr 19 '13 at 13:57
  • @Haspemulator : It seems your answer is really a promising one. But i tried the same in a list box with around 300 items. But while the time of scrolling i am getting an unauthorized exception from Isolated storage object. I think if you only have a single image control to bind there is no advantage with this method. If it works along with list box. Then it would be great. Waiting for your reply. – Nitha Paul Apr 20 '13 at 02:43
  • @NithaPaul just checked on my Lumia 920 with ~300 pics and it works. More info about the exception is needed to solve your problem, and I guess it should be a separate question. – Haspemulator Apr 20 '13 at 11:33
  • @Haspemulator : If I try the same with default library images; it will work fine. But if I try the images that took by the camera. The app will crash, "An exception of type 'System.OutOfMemoryException' occurred in System.Windows.ni.dll but was not handled in user code" . This crash occurs in your code where you are setting the source. (ie in _img.SetSource(stream); – Nitha Paul Apr 22 '13 at 12:10
  • @Haspemulator : I added this issue as separate question. Please take a look into this http://stackoverflow.com/questions/16146906/app-crashes-while-trying-to-bind-the-isolated-storage-image – Nitha Paul Apr 22 '13 at 12:17
1

I like the above approach but there is a simpler more hacky way of doing it if you are interested.

You can go into your xaml and bind the image source to an string property then put the file path into the property dynamically.

<!-- XAML CODE -->
 <Image Source="{Binding imagePath}"/>


//Behind property
public String imagePath { get; set; }

load your path into the image path then bind the image source to the image path string. You might have to do an INotifyPropertyChanged but this method should work with proper binding.

DotNetRussell
  • 9,716
  • 10
  • 56
  • 111