Save image from an image box to gallery in Xamarin Forms - c#

Is there a way to save the picture in an image control to the Android gallery in Xamarin Forms? All help is appreciated.
var image = new Image();
image.Source = "test.png";
Screenshot

You could use Dependency Service to get the stream from Resource/drawable image.
Create the IDependency interface.
public interface IDependency
{
MemoryStream DrawableByNameToByteArray(string fileName);
}
Android implementation
public class DependencyImplementation : IDependency
{
public MemoryStream DrawableByNameToByteArray(string fileName)
{
var context = Application.Context;
using (var drawable = Xamarin.Forms.Platform.Android.ResourceManager.GetDrawable(context, fileName))
using (var bitmap = ((BitmapDrawable)drawable).Bitmap)
{
var stream = new MemoryStream();
bitmap.Compress(Bitmap.CompressFormat.Png, 100, stream);
bitmap.Recycle();
return stream;
}
}
}
For the IOS implementation, you could refer to the thread in SO.
Convert Image (from drawable folder) to ByteArray
And then register in Android Mainactivity.
DependencyService.Register<IDependency, DependencyImplementation>();
If your android version is highter than android 6.0, you need runtime permission for storage in this question. Please check the Plugin.Permissions with runtime permission.
https://github.com/jamesmontemagno/PermissionsPlugin
After that, you could save the image to picture internal storage.
var filename = "";
var source = image.Source as FileImageSource;
if (source != null)
{
filename = source.File;
}
var savingFile = System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyPictures);
//var savingFile1 = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData), "save.png");
var S = DependencyService.Get<IDependency>().DrawableByNameToByteArray(filename);
if (!File.Exists(savingFile))
{
File.WriteAllBytes(savingFile, S.ToArray());
}
In Internal Storage, you couldn't see the files without root permission.
If you want to view it, you could use adb tool.
Please check the way in link.
How to write the username in a local txt file when login success and check on file for next login?

You can use Media Plugin and it can solve your issue. https://github.com/jamesmontemagno/MediaPlugin
You can visit the above link.
takePhoto.Clicked += async (sender, args) =>
{
await CrossMedia.Current.Initialize();
if (!CrossMedia.Current.IsCameraAvailable || !CrossMedia.Current.IsTakePhotoSupported)
{
DisplayAlert("No Camera", ":( No camera available.", "OK");
return;
}
var file = await CrossMedia.Current.TakePhotoAsync(new Plugin.Media.Abstractions.StoreCameraMediaOptions
{
Directory = "Sample",
Name = "test.jpg"
});
if (file == null)
return;
await DisplayAlert("File Location", file.Path, "OK");
image.Source = ImageSource.FromStream(() =>
{
var stream = file.GetStream();
return stream;
});
};

Related

Xamarin Forms saving image in sqlite database

How do I save an image I captured directly to sqlite(sqlite-net-pcl) database?
Here's my code but it can only save in "Internal Storage" of the phone.
private async void TakePhotoButton_Clicked(object sender, EventArgs e)
{
try
{
await CrossMedia.Current.Initialize();
if (!CrossMedia.Current.IsCameraAvailable || !CrossMedia.Current.IsTakePhotoSupported)
{
await DisplayAlert("No Camera", "No Camera Available", "OK");
return;
}
var file = await CrossMedia.Current.TakePhotoAsync(
new StoreCameraMediaOptions
{
SaveToAlbum = true,
//Directory = "Sample",
//Name = "Test.jpg"
});
if (file == null)
return;
PathLabel.Text = file.AlbumPath;
MainImage.Source = ImageSource.FromStream(() =>
{
var stream = file.GetStream();
file.Dispose();
return stream;
});
}
catch (Exception ex)
{
await DisplayAlert("error", ex.ToString(), "OK");
}
}
You should convert the stream to Byte array so that you save them to sqlite.
public byte[] GetImageStreamAsBytes(Stream input)
{
var buffer = new byte[16*1024];
using (MemoryStream ms = new MemoryStream())
{
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
return ms.ToArray();
}
}
var imgDate = GetImageStreamAsBytes(file.GetStream());
Concerning the SaveToAlbum option
This will restult in 2 photos being saved for the photo. One in your private folder and one in a public directory that is shown. The value will be returned at AlbumPath. (Source)
Unless you really need the photo in the camera roll, there is no need to use SaveToAlbum.
Anyway, the file is still saved to your apps sandbox (the part of the devices storage that is reserved exclusively for your app) and you can retrieve the path with file.Path.
Having said that, it's easy to obtain the binary data representing your image
await CrossMedia.Current.Initialize();
if (!CrossMedia.Current.IsCameraAvailable || !CrossMedia.Current.IsTakePhotoSupported)
{
await DisplayAlert("No Camera", "No Camera Available", "OK");
return;
}
var file = await CrossMedia.Current.TakePhotoAsync(new StoreCameraMediaOptions());
if (file == null)
return;
var imageData = File.ReadAllBytes(file.Path);
Obviously (see here and here) it's possibly to store binary data (BLOBs) in an SQLite database. The simplest conceivable model to store an image in the database would be something like
class Image
{
[PrimaryKey, AutoIncrement]
public int ID { get; set; }
public byte[] Data { get; set; }
}
Assuming that _imageRepository is your repository abstraction you are saving the images in, the data could be saved as
// ...
var file = await CrossMedia.Current.TakePhotoAsync(new StoreCameraMediaOptions());
if (file == null)
return;
var imageData = File.ReadAllBytes(file.Path);
_imageRepository.Add(new Image()
{
Data = imageData
});
Later on, to display the image, you can get the Image from the repository and use the Data property, e.g. by passing it to a MemoryStream (if you need a stream)
// example: Loading by ID, loading all images is conceivable, too
var image = _imageRepository.LoadImage(id);
ImageControl.ImageSource = ImageSource.FromStream(() => new MemoryStream(image.Data));

Xamarin Saving an image and displaying in navigation bar

I am trying to save an image to the internal storage of an Android device. Then I want to access it in the shared project for the icons in my navigation bar. The image is coming back from an API. Its giving Android.Content.Res.Resources+NotFoundException Resource ID #0x0.
Saving the image:
public async Task<bool> SaveImage(string filename, ImageSource img)
{
System.IO.Stream outputStream = null;
bool success = false;
var renderer = GetHandler(img);
Bitmap photo = Task.Run(async () => await renderer.LoadImageAsync(img, Forms.Context)).Result;
string savedImageFilename = System.IO.Path.Combine(documentsPath, filename);
using (outputStream = new System.IO.FileStream(savedImageFilename, FileMode.Create))
{
if (System.IO.Path.GetExtension(filename).ToLower() == ".png")
success = await photo.CompressAsync(Bitmap.CompressFormat.Png, 100, outputStream);
else
success = await photo.CompressAsync(Bitmap.CompressFormat.Jpeg, 100, outputStream);
}
return success;
}
Loading the Image:
public FileImageSource LoadImage(string filename)
{
var filePath = Path.Combine(documentsPath, MyBlueAssets);
filePath = Path.Combine(filePath, filename);
FileImageSource image = (FileImageSource)ImageSource.FromFile(filePath);
return image;
}
Accessing and displaying in shared project:
DependencyService.Get<IImageCacheDependency>().SaveImage(imageName, Image, count);
FileImageSource loadedImage = DependencyService.Get<IImageCacheDependency ().LoadImage(imageName);
navigation.Icon = loadedImage;

Trying To take Screenshot in uwp programatically on tapping of an image

trying to capture screenshot on uwp But some issue is arising,saying
1) writable map doesn't contain a definition for Render.
2) writable map doesn't contain a definition for Save.jpeg
3)Type or namespace media library doesn't found.
any type of help would be appreciated.
Code
private void Download(object sender, TappedRoutedEventArgs e)
{
// Create a WriteableBitmap with height and width same as that of Layoutroot
WriteableBitmap bmp = new WriteableBitmap(480, 696);
//Render the layoutroot element on it
bmp.Render(Scrshot, null);
bmp.Invalidate();
//Save the image to Medialibrary
//create a stream of image
var ms = new MemoryStream();
bmp.SaveJpeg(ms, 480, 696, 0, 100);
ms.Seek(0, SeekOrigin.Begin);
//every path must be unique
var filePath = "myfile" + DateTime.Now.ToString();
//Remember to include Medialib capabilty from WMAppManifest.xml for acessing the Medialibrary
var lib = new MediaLibrary();
lib.SavePicture(filePath, ms);
MessageBox.Show("Saved in your media library!", "Done", MessageBoxButton.OK);
}
Instead of WriteableBitmap use RenderTargetBitmap to create a bitmap representation of a visible UIElement. To save this bitmap as a file you can use this extension method I've created (here's a great example for extension methods):
public static class RenderTargetBitmapExtensions {
public static async Task<StorageFile> ToFile(this RenderTargetBitmap renderTargetBitmap, string filename, StorageFolder folder = null, bool overrideExisting = true) {
if (folder == null) folder = ApplicationData.Current.TemporaryFolder;
try {
byte[] pixels = (await renderTargetBitmap.GetPixelsAsync()).ToArray();
StorageFile outputFile = await folder.CreateFileAsync(filename, overrideExisting ? CreationCollisionOption.ReplaceExisting : CreationCollisionOption.GenerateUnique);
var bitmapEncodingMode = BitmapEncoder.PngEncoderId;
using (var writeStream = await outputFile.OpenAsync(FileAccessMode.ReadWrite)) {
var encoder = await BitmapEncoder.CreateAsync(bitmapEncodingMode, writeStream);
encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Straight, (uint)renderTargetBitmap.PixelWidth, (uint)renderTargetBitmap.PixelHeight, 96, 96, pixels);
await encoder.FlushAsync();
}
return outputFile;
} catch {
return null;
}
}
}
And finally to get the public Images folder (as MediaLibrary is a Silverlight class and does not exist in UWP anymore) you can do the following according to this thread:
StorageFolder picturesLibrary = KnownFolders.PicturesLibrary;
StorageFolder savedPicturesFolder = await picturesLibrary.CreateFolderAsync("Saved Pictures", CreationCollisionOption.OpenIfExists);
Note: By default, apps do not have access to the Pictures Library. You must add the capability in Package.appxmanifest. Open Package.appxmanifest and click on the Capabilities tab. There is a checkbox for the Pictures Library.
So a whole code to do this should be:
RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap();
await renderTargetBitmap.RenderAsync(LayoutRoot, Convert.ToInt32(LayoutRoot.ActualWidth), Convert.ToInt32(LayoutRoot.ActualHeight));
StorageFolder savedPicturesFolder = await KnownFolders.PicturesLibrary.CreateFolderAsync("Saved Pictures", CreationCollisionOption.OpenIfExists);
await renderTargetBitmap.ToFile("filename.jpg", savedPicturesFolder);
Or if you don't want to override existing files, the last line would be:
await renderTargetBitmap.ToFile("filename.jpg", savedPicturesFolder, false);
For that you can alternatively create a time-based filename:
string filename = String.Format("downloaded_{0}.jpg", DateTime.Now.ToString("yyyyMMdd_HHmmss"));
await renderTargetBitmap.ToFile(filename, savedPicturesFolder, false);

get path of selected image by fileopenpicker windows phone 8.1 C#

I got an image by using FileOpenPicker. I want to save the path of the image in database, so that by using that path I can get that image any other screen,,
Now I simply send my image path to second page. but with the same Path I'm not able to get image.
Here is my code.
private async void button_Click(object sender, RoutedEventArgs e)
{
ImagePath = string.Empty;
FileOpenPicker filePicker = new FileOpenPicker();
filePicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
filePicker.ViewMode = PickerViewMode.Thumbnail;
filePicker.FileTypeFilter.Clear();
filePicker.FileTypeFilter.Add(".bmp");
filePicker.FileTypeFilter.Add(".png");
filePicker.FileTypeFilter.Add(".jpeg");
filePicker.FileTypeFilter.Add(".jpg");
filePicker.PickSingleFileAndContinue();
view.Activated += viewActivated;
}
private async void viewActivated(CoreApplicationView sender, IActivatedEventArgs args1)
{
FileOpenPickerContinuationEventArgs args = args1 as FileOpenPickerContinuationEventArgs;
if (args != null)
{
if (args.Files.Count == 0) return;
view.Activated -= viewActivated;
StorageFile storageFile = args.Files[0];
var stream = await storageFile.OpenAsync(Windows.Storage.FileAccessMode.Read);
**//ImagePath is global string. I get the image path here**
ImagePath = storageFile.Path;
var bitmapImage = new Windows.UI.Xaml.Media.Imaging.BitmapImage();
await bitmapImage.SetSourceAsync(stream);
var decoder = await Windows.Graphics.Imaging.BitmapDecoder.CreateAsync(stream);
//image is my image control in Xaml.
image.Source = bitmapImage;
}
}
private void image_click(object sender, TappedRoutedEventArgs e)
{
this.Frame.Navigate(typeof(detail), ImagePath);
}
By tapping on Image I move to detail screen. where I have another image control where I wan to show the same image ,,,,getting through path.
My Detail screen code is:
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
var imagePath = e.Parameter as string;
image2.Source = new BitmapImage(new Uri(imagePath, UriKind.RelativeOrAbsolute));
}
The above code didn't work.
Then I tried another way,,
var file = await Windows.Storage.StorageFile.GetFileFromApplicationUriAsync(new Uri(imagePath));
Stream stream = await file.OpenStreamForReadAsync();
using (var memStream = new MemoryStream())
{
await stream.CopyToAsync(memStream);
memStream.Position = 0;
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(memStream.AsRandomAccessStream());
// create a new stream and encoder for the new image
InMemoryRandomAccessStream mrAccessStream = new InMemoryRandomAccessStream();
BitmapEncoder encoder = await BitmapEncoder.CreateForTranscodingAsync(mrAccessStream, decoder);
// convert the bitmap to a 400px by 600px bitmap
encoder.BitmapTransform.ScaledHeight = 400;
encoder.BitmapTransform.ScaledWidth = 600;
try
{
await encoder.FlushAsync();
}
catch (Exception ex)
{
string s = ex.ToString();
}
// render the stream to the screen
WB = new WriteableBitmap(500, 500);
WB.SetSource(mrAccessStream);
image2.Source = WB;
This also didn't work.
I think the problem is in path. I got path awkward path like
"C:\\Data\\Users\\Public\\Pictures\\Saved Pictures\\Image-130872081698409356.jpeg".
Please help me to get rid of this problem.
This is by design and it is a security issue. Imagine what would happen if just knowing the path to a file you could access that file. The security model of store apps would be broken.
Note that is the file picker that provides you with a file that you have access to. You can start from a file picker to get access to a file. You cannot start from a path to get access to a file. A file picker guarantees that the user will be involved and hence aware of what is going on. If you were allowed to start from a path and programmatically access a file that would violate the security rules for app stores.

Sharing screenshot on Windows Phone 8.1 WinRT does not attach image on sharing apps

I want to share the screenshot of my app. The screenshot is saved into the phone's picture library then it's shared as StorageFile
The problem is, the image is not attached to the sharing apps. I've verified that the screenshot was saved successfully in the phone's picture library.
Here's my code. What am I missing?
private async void askFacebook()
{
// Render some UI to a RenderTargetBitmap
var renderTargetBitmap = new RenderTargetBitmap();
await renderTargetBitmap.RenderAsync(this.gridRoot, (int)this.gridRoot.ActualWidth, (int)this.gridRoot.ActualHeight);
// Get the pixel buffer and copy it into a WriteableBitmap
var pixelBuffer = await renderTargetBitmap.GetPixelsAsync();
var width = renderTargetBitmap.PixelWidth;
var height = renderTargetBitmap.PixelHeight;
var wbmp = await new WriteableBitmap(1, 1).FromPixelBuffer(pixelBuffer, width, height);
imageToShare = await saveWriteableBitmapAsJpeg(wbmp, string.Format("{0}.jpg", getAppTitle()));
DataTransferManager.ShowShareUI();
}
private string getAppTitle()
{
// Get the assembly with Reflection:
Assembly assembly = typeof(App).GetTypeInfo().Assembly;
// Get the custom attribute informations:
var titleAttribute = assembly.CustomAttributes.Where(ca => ca.AttributeType == typeof(AssemblyTitleAttribute)).FirstOrDefault();
// Now get the string value contained in the constructor:
return titleAttribute.ConstructorArguments[0].Value.ToString();
}
private async Task<StorageFile> saveWriteableBitmapAsJpeg(WriteableBitmap bmp, string fileName)
{
// Create file in Pictures library and write jpeg to it
var outputFile = await KnownFolders.PicturesLibrary.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting);
using (var writeStream = await outputFile.OpenAsync(FileAccessMode.ReadWrite))
{
await encodeWriteableBitmap(bmp, writeStream, BitmapEncoder.JpegEncoderId);
}
return outputFile;
}
private async Task encodeWriteableBitmap(WriteableBitmap bmp, IRandomAccessStream writeStream, Guid encoderId)
{
// Copy buffer to pixels
byte[] pixels;
using (var stream = bmp.PixelBuffer.AsStream())
{
pixels = new byte[(uint)stream.Length];
await stream.ReadAsync(pixels, 0, pixels.Length);
}
// Encode pixels into stream
var encoder = await BitmapEncoder.CreateAsync(encoderId, writeStream);
var logicalDpi = DisplayInformation.GetForCurrentView().LogicalDpi;
encoder.SetPixelData(BitmapPixelFormat.Bgra8,
BitmapAlphaMode.Premultiplied,
(uint)bmp.PixelWidth,
(uint)bmp.PixelHeight,
logicalDpi,
logicalDpi,
pixels);
await encoder.FlushAsync();
}
private void ShareImageHandler(DataTransferManager sender, DataRequestedEventArgs e)
{
DataRequest request = e.Request;
request.Data.Properties.Title = "Ask Social Media";
request.Data.Properties.Description = "Do you know the answer to this question?";
// Because we are making async calls in the DataRequested event handler,
// we need to get the deferral first.
DataRequestDeferral deferral = request.GetDeferral();
// Make sure we always call Complete on the deferral.
try
{
request.Data.SetBitmap(RandomAccessStreamReference.CreateFromFile(imageToShare));
}
finally
{
deferral.Complete();
}
}
Apparently for Windows Phone, the image needs to be a StorageItem as the SetBitmap methods only works with Windows 8.x
So for Windows phone, instead of
request.Data.SetBitmap(RandomAccessStreamReference.CreateFromFile(imageToShare));
I created a storage item and use SetStorageItems to share it. It works with native Windows Phone apps such as emails, OneNote but I haven't tested it for sharing on Facebook, Twitter etc.
var imageItems = new List<IStorageItem>();
imageItems.Add(imageToShare);
request.Data.SetStorageItems(imageItems);

Categories