dub stylee
dub stylee

Reputation: 3342

WinRT binding an Image to a string or StorageFile

I am trying to display an image from a StorageFile after selecting it from a FilePicker. Since the Source of an Image has to be either a URI or an ImageSource, I am trying to get either of those from the StorageFile.

I am having a hard time getting data binding to work on an Image in XAML. I have tried the following:

<Image Width="300" 
       Height="300" 
       x:Name="LogoImage">
    <Image.Source>
        <BitmapImage UriSource="{Binding ImagePath}" />
    </Image.Source>
</Image>

This way doesn't work, as the Path property of a StorageFile is not a URI. Also, I can't bind directly to the StorageFile itself, as it is not an ImageSource.

I tried to use this method:

public async Task<Image> GetImageAsync(StorageFile storageFile)
{
    BitmapImage bitmapImage = new BitmapImage();
    FileRandomAccessStream stream = (FileRandomAccessStream)await storageFile.OpenAsync(FileAccessMode.Read);
    bitmapImage.SetSource(stream);
    Image image = new Image();
    image.Source = bitmapImage;
    return image;
}

But, it returns a Task<Image>, which is also not an ImageSource or URI. It seems like it should be something more straightforward than what I am trying to do, but I am just not seeing it. Also, I have tried just specifying a file in the XAML for the Image.Source and it works fine. I just haven't been able to link it up based on the selected file from the FilePicker.

My ultimate goal is: select a file from FilePicker, update the ImageSource of the displayed Image, encode to base64 for storage in database. Then later, load existing base64 string from database, convert back to Image to be displayed.

Edit:

I was able to accomplish this task using the solution I posted below. Thanks in great part to Jerry Nixon's blog post: http://blog.jerrynixon.com/2014/11/reading-and-writing-base64-in-windows.html

Upvotes: 0

Views: 1382

Answers (2)

dub stylee
dub stylee

Reputation: 3342

If anyone else comes across this question looking for an answer, what I ultimately ended up doing for my situation, is take the StorageFile that is selected from the FilePicker, encode that as a base64 string (which I will save to my database for later retrieval).

Once I have the base64 string, I can decode that string into an ImageSource to set in code-behind. Here is my full ButtonClick event handler:

private async void ChooseImage_Click(object sender, RoutedEventArgs e)
{
    var filePicker = new FileOpenPicker();
    filePicker.FileTypeFilter.Add(".jpg");
    filePicker.ViewMode = PickerViewMode.Thumbnail;
    filePicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
    filePicker.SettingsIdentifier = "picker1";
    filePicker.CommitButtonText = "Open File to Process";

    var files = await filePicker.PickMultipleFilesAsync();

    if (files.Count > 0)
    {
        // encode the storage file to base64
        var base64 = await Encoding.ToBase64(files[0]);

        // asynchronously save base64 string to database
        // ...

        // decode the base64 and set the image source
        LogoImage.Source = await Encoding.FromBase64(base64);
    }
}

The base64 encoding/decoding I found at the great Jerry Nixon's blog here: http://blog.jerrynixon.com/2014/11/reading-and-writing-base64-in-windows.html

Upvotes: 0

Filip Skakun
Filip Skakun

Reputation: 31724

The best solution would be to set the source in code behind instead of using a binding as it would let you handle things like cancellation in case the ImagePath gets updated while the previous image is still loading.

Alternatively, you could create a bitmap, start a task of loading it and return before that task is complete, e.g.

public Image GetImage(StorageFile storageFile)
{
    var bitmapImage = new BitmapImage();
    GetImageAsync(bitmapImage, storageFile);

    // Create an image or return a bitmap that's started loading
    var image = new Image();
    image.Source = bitmapImage;

    return image ;
}

private async Task GetImageAsync(BitmapImage bitmapImage, StorageFile storageFile)
{
    using (var stream = (FileRandomAccessStream)await storageFile.OpenAsync(FileAccessMode.Read))
    {
        await bitmapImage.SetSource(stream);
    }
}

Upvotes: 2

Related Questions