Windows phone 8.1 image loading on UI - c#

We are developing an app which have to attach an image at the runtime and display the same to the UI. We are using following set of code file for it:
private void Btn_Attach_File_Click(object sender, RoutedEventArgs e)
{
FileOpenPicker fileOpenPicker = new FileOpenPicker();
fileOpenPicker.FileTypeFilter.Add(".jpg");
fileOpenPicker.FileTypeFilter.Add(".jpeg");
fileOpenPicker.FileTypeFilter.Add(".png");
fileOpenPicker.ContinuationData["Operate"] = "OpenImage";
fileOpenPicker.PickSingleFileAndContinue();
}
Also, to continue the app after picking file from storage, here is the code :
public async void ContinueFileOpenPicker(FileOpenPickerContinuationEventArgs args)
{
if ((args.Files != null && args.Files.Count > 0))
{
IReadOnlyList<StorageFile> files = args.Files;
Image myImage = new Image();
foreach (StorageFile file in files)
{
if (args.ContinuationData["Operate"] as string == "OpenImage" && args.Files != null && args.Files.Count > 0)
{
IRandomAccessStream fileStream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read);
System.Diagnostics.Debug.WriteLine(file.Name);
System.Diagnostics.Debug.WriteLine(file.Path);
using (StorageItemThumbnail thumbnail = await file.GetThumbnailAsync(ThumbnailMode.SingleItem, 190)) //, ThumbnailOptions.UseCurrentScale);
{
if (thumbnail != null && thumbnail.Type == ThumbnailType.Image)
{
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.SetSource(thumbnail);
myImage.Source = bitmapImage;
}
}
}
}
}
}
Above code is in xaml.cs file.
But the problem is, even though I'm loading the file, there is no attachment on UI.
Do we need to make any changes to the corresponding xaml file? we've searched a lot on it but couldn't find any solution.
Thanks in advance,

First, make an image control in xaml:
<Image Name="img"/>
In codebehind after the loop:
this.img.Source = myImage.Source;
//For better perf according to the docs, specify this
//this.img.Source.DecodePixelWidth = 200
Certainly it's possible to make a binding instead of using the Name property, but this is the easiest way.
Read the docs: MSDN
One other thing, the loop foreach (StorageFile file in files) isn't that useful if you have one file, and if you have more than one file it will take the last file.
Feel free to ask for more info!
Happy coding :)!

Related

Xaml Image.Source is not updating in C#

I have some trouble with updating the source of an Image in Xaml. Im making a Windows Store App, and Im trying to set the source in my C# code. Basically, what my small program is doing is to let the user select a JPG file, and then copy this over to AppData folder. In my App, I want the picture the user have uploaded to show. Everything is working except the part where I show the image, this image does not want to change even if I provide a new source.
C# Code:
public sealed partial class MainPage : Page
{
FileOpenPicker pickerSelect;
FileSavePicker pickerSave;
StorageFolder folder;
StorageFile pic;
public MainPage()
{
this.InitializeComponent();
InitializePickers();
InitializeProfilePicture();
}
private async void InitializeProfilePicture()
{
folder = Windows.Storage.ApplicationData.Current.LocalFolder;
pic = await folder.GetFileAsync("profile.jpg");
BitmapImage uri = new BitmapImage(new Uri(pic.Path, UriKind.Absolute));
ProfilePic.Source = uri;
}
private void InitializePickers()
{
pickerSelect = new FileOpenPicker();
pickerSave = new FileSavePicker();
pickerSelect.FileTypeFilter.Add(".jpg");
}
private async void Upload_Click(object sender, RoutedEventArgs e)
{
StorageFile pictureSelect = await pickerSelect.PickSingleFileAsync();
StorageFolder folder = Windows.Storage.ApplicationData.Current.LocalFolder;
await pictureSelect.CopyAsync(folder, "profile.jpg", NameCollisionOption.ReplaceExisting);
InitializeProfilePicture();
}
}
In the method "InitializeProfilePicture" I create a new BitmapImage and I set ProfilePic to this. This code is just working once, if I run the InitializeProfilePicture in the start as I do now, and the user selects a new picture and uploads to the AppData folder, the image does not change (even though the picture is indeed uploaded). If I remove the method from the start and just keep it in the Button_Click method, the new uploaded picture will be the one showing. But uploading a new picture after ProfilePic have set it's source, it will not change.
Image in Xaml is just like this
Image Width="480" Height="640" x:Name="ProfilePic" Grid.Row="1" Grid.RowSpan="2"
.. And there is also a button here to run Upload_Click method, but thats it.
Any idea why this is happening?? Should it not be updated??
You could load a BitmapImage directly from file like this:
var imageFile = await picker.PickSingleFileAsync();
var bitmap = new BitmapImage();
using (var stream = await imageFile.OpenReadAsync())
{
await bitmap.SetSourceAsync(stream);
}
ProfilePic.Source = bitmap;

Getting the bytes[] / stream of an Image or ImageSource in Xamarin.Forms?

I've been digging into Xamarin.Forms.Labs to learn more about how Images are accessed and manipulated. Saving an image to an Android/iOS photo gallery works fine, as does retrieving an image from either, at least with user interaction.
The problem is that I'd like to be able to save some files internally to the program. Actually saving a file itself doesn't seem to be a problem - I've written up an interface/DependencyService solution for that.
What I can't seem to do is access the bytes[] or stream of the image data itself with a Xamarin.Forms Image or ImageSource. Reading a stream into the ImageSource is relatively straightforward with the static method, so how do I get these bytes out in order to save the file within the program itself?
To frame this: I'm working on an app right now where the user takes/selects pictures to include within a form, and the form is eventually posted to a website. So being able to actually save the pictures, or access the data itself in order to transfer it, is pretty key.
One really hacky way to do that would be to write a custom renderer for your image. Then the renderer will get the byte[]/Stream from the native control. Here's a very rough implementation without any error handling to help you get started:
public class MyImage : Image
{
public Func<byte[]> GetBytes { get; set; }
}
Here's the iOS renderer:
public class MyImageRenderer : ImageRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Image> e)
{
base.OnElementChanged(e);
var newImage = e.NewElement as MyImage;
if (newImage != null)
{
newImage.GetBytes = () =>
{
return this.Control.Image.AsPNG().ToArray();
};
}
var oldImage = e.OldElement as MyImage;
if (oldImage != null)
{
oldImage.GetBytes = null;
}
}
}
The Android one is a little more involved:
public class MyImageRenderer : ImageRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Image> e)
{
base.OnElementChanged(e);
var newImage = e.NewElement as MyImage;
if (newImage != null)
{
newImage.GetBytes = () =>
{
var drawable = this.Control.Drawable;
var bitmap = Bitmap.CreateBitmap(drawable.IntrinsicWidth, drawable.IntrinsicHeight, Bitmap.Config.Argb8888);
drawable.Draw(new Canvas(bitmap));
using (var ms = new MemoryStream())
{
bitmap.Compress(Bitmap.CompressFormat.Png, 100, ms);
return ms.ToArray();
}
};
}
var oldImage = e.OldElement as MyImage;
if (oldImage != null)
{
oldImage.GetBytes = null;
}
}
}
And finally, the usage is the kind of obvious:
var bytes = myImage.GetBytes?.Invoke();
Extending the idea to support streams should be pretty straightforward. The obvious caveat is that the forms control (MyImage) retains a reference to its renderer via GetBytes(), but that seems to be unavoidable.

Out of memory exception after selecting images

I want to show a list of selected images by user:
void photoChooserTask_Completed(object sender, PhotoResult e)
{
if (e.TaskResult == TaskResult.OK)
{
var fileName = e.OriginalFileName;
var photoStream = e.ChosenPhoto;
}
}
and
<Image Source="{Binding PhotoStream}" Width="200" Height="200"/>
the problem is after selecting 4 or 5 images, app hits memory limit and exits. I just want to show thumbnails of those selected images, like PhotoHub, how can I do that without consuming a lot of memory? thanks
As mentioned in the comments there are several ways to solve it, and as you mention you will probably need a thumbnail. Here are some methods I can think of right now which might solve your memory problem:
Method 1:
First using the MediaLibrary to find the same image, where you will get a stream to a thumbnail version, like this:
void task_Completed(object sender, Microsoft.Phone.Tasks.PhotoResult e)
{
if (e.TaskResult == Microsoft.Phone.Tasks.TaskResult.OK)
{
MediaLibrary library = new MediaLibrary();
Picture pic = library.Pictures.Where(p => e.OriginalFileName.EndsWith("\\" + p.Album.Name + "\\" + p.Name)).FirstOrDefault();
Stream thumbnailStream = pic.GetThumbnail(); // Stream to a thumbnail
}
}
For this to work you will also need to enabled the capability ID_CAP_MEDIALIB_PHOTO in the WMAppManifest.xml or you wont get any results.
Method 2:
The second option would be to use for example the WriteableBitmapEx library to create a thumbnail yourself, something along the lines of:
void task_Completed(object sender, Microsoft.Phone.Tasks.PhotoResult e)
{
if (e.TaskResult == Microsoft.Phone.Tasks.TaskResult.OK)
{
BitmapImage source = new BitmapImage();
source.SetSource(e.ChosenPhoto);
WriteableBitmap bitmap = new WriteableBitmap(source);
WriteableBitmap thumbnail = bitmap.Resize(100, 100, WriteableBitmapExtensions.Interpolation.Bilinear); // Creates a 100x100 thumbnail
}
}
Other methods:
Another solution might be using the Nokia Imaging SDK, which according to the documentation supports partial JPEG decoding (I haven't used this SDK myself so can't give you any example code right now though):
Using RAJPEG technology, access image data without decoding a whole
JPEG image for blazingly fast previews, application of effects,
rotation, and cropping of high resolution images.

Display loading while Caching image from URL

I have a code in WPF C# that i use to load images from the web as such:
if (myImgURL != "")
{
var imgBitmap = new BitmapImage();
imgBitmap.BeginInit();
imgBitmap.UriSource = new Uri(myImgURL, UriKind.RelativeOrAbsolute);
imgBitmap.CacheOption = BitmapCacheOption.OnLoad;
imgBitmap.EndInit();
myImgControl.Source = imgBitmap;
}
It works perfectly but sometimes it takes a while before the images get displayed (if internet is slow and all). How can I have a ProgressRing (from the Mahapps.Metro toolkit) display and be enabled while the image loads and then disappear when the image is displayed?
I am not aware of any event trigger for when an image is being downloaded and when it is fully loaded.
Take a look at the following events in class BitmapSource (the base class of BitmapImage):
DownloadProgress
DownloadCompleted
DownloadFailed
And just a note. You are creating a BitmapImage from an Uri and showing it immediately. Hence there is no need to set BitmapCacheOption.OnLoad (which afaik is necessary only if you load from a stream that should be closed immediately after EndInit). So you could shorten your code like this:
if (!string.IsNullOrEmpty(myImgURL))
{
var imgBitmap = new BitmapImage(new Uri(myImgURL));
myImgControl.Source = imgBitmap;
if (imgBitmap.IsDownloading)
{
// start download animation here
imgBitmap.DownloadCompleted += (o, e) =>
{
// stop download animation here
};
imgBitmap.DownloadFailed += (o, e) =>
{
// stop download animation here
};
}

How do I embed an Image into a RichTextBox?

I am looking for a way to embed image to Rich text box. My rtf file is portable which contains both image and text. i.e. It can be moved from one computer to another. So the user should be able to see the content of rtf file (text+image) even if its in another machine.
Now I am using the following code to insert image.
public static void ApplyImage(RichTextBox RichTextBoxControl)
{
try
{
Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
dlg.DefaultExt = ".png";
dlg.Filter = "PNG Files (.png)|*.png|JPG Files (.jpg)|*.jpg|GIF Files (.gif)|*.gif";
Nullable<bool> result = dlg.ShowDialog();
if (result == true)
{
string fileName = dlg.FileName;
if (File.Exists(fileName))
{
BitmapImage bi = new BitmapImage(new Uri(fileName));
Image image = new Image();
image.Source = bi;
InlineUIContainer container = new InlineUIContainer(image);
Paragraph paragraph = new Paragraph(container);
RichTextBoxControl.Document.Blocks.Add(paragraph);
}
}
}
catch
{
throw;
}
}
But this code is not suitable for my purpose. Because the embedded image may not be in the other machine. So it won't work. Either I need to embed image or store image as binary in rtf file. I searched everywhere and no luck.
Can anyone help me please?
I got a solution for the problem. I am not sure its the right way, but still this solution is working for me. Hope it may help others.
The solution is to iterate through all image tags in flow document and copy the images to a repository folder which is portable. Then load the images from that repository folder next time to show the images back in to richtextbox.
foreach (Block block in rtb.Blocks)
{
if (block is Paragraph)
{
Paragraph paragraph = (Paragraph)block;
foreach (Inline inline in paragraph.Inlines)
{
if (inline is InlineUIContainer)
{
InlineUIContainer uiContainer = (InlineUIContainer)inline;
if (uiContainer.Child is Image)
{
Image image = (Image)uiContainer.Child;
//Copy Image source to another repository folder and save the path.
}
}
}
}

Categories