SendETHToThisAddress
SendETHToThisAddress

Reputation: 3704

Xamarin save image from imageButton

I have a page with an image button that displays a blank profile pic. The user can tap on it and it opens up a file dialog to select a picture from their gallery. After selecting a picture, the user sees the image displayed on the image button on the page. I set the image source like this:

private async void ImgStudentImage_Clicked(object sender, EventArgs e)
{
    Stream stream = await DependencyService.Get<IPhotoPickerService>().GetImageStreamAsync();
    if (stream != null)
    {
        imgButton.Source = ImageSource.FromStream(() => stream);
    }

}

This is all good, but when the save button is clicked/tapped how do I save the image? I was thinking that upon clicking the save button I could access the image using something like

string pth = imgButton.Source.ToString();
std.Picture = File.ReadAllBytes(pth);

Then I could save the image as a byte array, but .Source.ToString() returns "Xamarin.Forms.StreamingImageSource" and it throws this error:

System.IO.FileNotFoundException
Could not find file 'C:\Xamarin\DJB\DJB\DJB.UWP\bin\x86\Debug\AppX\Xamarin.Forms.StreamImageSource'.

Is there a way to retrieve the image from the imageButton?

Upvotes: 0

Views: 720

Answers (3)

Tanyel
Tanyel

Reputation: 63

Had the same problem. That the image would not been shown if i copy the source to a memory stream, Because the copy to method sets the position of the stream to the last digit. You just need to put it on 0;

private async void ImgStudentImage_Clicked(object sender, EventArgs e)
{
    Stream stream = await DependencyService.Get<IPhotoPickerService>().GetImageStreamAsync();
    if (stream != null)
    {
        MemoryStream ms = new MemoryStream();
        stream.CopyTo(ms); 
        stream.Position = 0;
        source = ImageSource.FromStream(() => stream);
       
    }
}

Upvotes: 0

SendETHToThisAddress
SendETHToThisAddress

Reputation: 3704

It took me a long time to find a solution to this answer, it would be really nice if Microsoft would add a method to the ImageButton to retrieve the image. After searching around for a while, getting more familiar with the data types, and looking at the data I already had when initially getting the image from the user, I decided to basically extend the ImageButton control to have a byte array attribute, and set that attribute when the user selects the image. This makes it very easy for me to work with. Instead of getting the image from the ImageButton as some random data type I can simply access the byte array from the ImageButton, which is the end data type I need.

Here is how I did it : In the method where I was retrieving the photo from the user (adapted code from Microsoft example)

private async void ImgStudentImage_Clicked(object sender, EventArgs e)
{
    Stream stream = await DependencyService.Get<IPhotoPickerService>().GetImageStreamAsync();
    if (stream != null)
    {
        imgButton.Source = ImageSource.FromStream(() => stream);
    }
}

the service returns a stream object. The stream object is useful but somewhat clunky to work with. You can see that I'm assigning the image on the form (page) here, but when I would try to do anything else with it the picture would disappear on the form! So I could not assign the stream to the ImageButton then use the stream to create a byte array - quite a pain. The workaround was to convert the stream to a byte array and save that byte array in my ExtendedImageButton, then retrieve the image back from the byte array stored in the image button. Silly right? But it works. So my on_click method now looks like this:

private async void ImgStudentImage_Clicked(object sender, EventArgs e)
{
    Stream stream = await DependencyService.Get<IPhotoPickerService>().GetImageStreamAsync();
    if (stream != null)
    {
        MemoryStream ms = new MemoryStream();
        stream.CopyTo(ms); //this line causes the image on the form to disappear, no matter where the line is placed in code. Creating a copy of the stream doesn't solve the problem either.
        imgButton.SourceAsByteArray = ms.ToArray();
        imgButton.Source = Converters.ByteArrayToImageSource(imgButton.SourceAsByteArray);
    }
}

My ExtendedImageButton is extremely simple:

using Xamarin.Forms;

namespace MyNameSpace.Controls
{
    class ExtendedImageButton : ImageButton
    {
        public byte[] SourceAsByteArray { get; set; }

    }
}

As you can see I just added a property of byte array. I stored this file in my Controls folder. In my xaml I use this reference and tag:

xmlns:controls="clr-namespace:MyAppName.Controls"
<controls:ExtendedImageButton />

I needed a method to convert the byte array back to an image source:

public static ImageSource ByteArrayToImageSource(byte[] byteArr)
{
    Stream stream = new MemoryStream(byteArr);
    return ImageSource.FromStream(() => stream);
}

I saved this method in a class file I named "Converters".

Finally, in the code where I want to retrieve the image from the ImageButton I simply use this:

userPicture = imgButton.SourceAsByteArray;

Where userPicture is of course a byte array variable.

Upvotes: 1

FreakyAli
FreakyAli

Reputation: 16409

As of now, this cannot be done with pure XF what you have to do is convert this image to its respective native type and then convert them to your desired type.

For instance, in Android, you would do something like :

public static async Task<Bitmap> GetBitmapFromImageSourceAsync(ImageSource source, Context context)
{
    var handler = GetHandler(source);
    var returnValue = (Bitmap)null;
    returnValue = await handler.LoadImageAsync(source, context);
    return returnValue;
}

You can check my blog here for a full-fledged solution.

Note that the blog does not have byte[] conversion from the native types, But is easily available on Stack and Xamarin Forums.

Feel free to get back if you have queries

Good luck.

Upvotes: 0

Related Questions