WriteableBitmap memory leak in Windows Phone 8 - c#

I'm having a memory leak whenever I create any instance of a WriteableBitmap. I've tried multiple suggestions on stackoverflow and other forums, but nothing is working. The basic flow of my test app is this:
Select an image with the PhotoChooserTask
Use the Stream from the PhotoResult object to create a WriteableBitmap.
That's it. Nulling the variables and calling GC.Collect() only solves part of the problem. It keeps the app from allocating memory until the app crashes, but even though the objects have gone out of scope, there is always memory allocated for them until I select a new image. I can reproduce it with the default Windows Phone Direct3D with XAML App. The only modifications to the default project are the following:
MainPage.xaml.cs
public MainPage() {
InitializeComponent();
_photoChooserTask = new PhotoChooserTask();
_photoChooserTask.Completed += new EventHandler<PhotoResult>(photoChooserTaskComplete);
}
private void ApplicationBarIconButton_Click(object sender, EventArgs e) {
_photoChooserTask.Show();
}
private void photoChooserTaskComplete(object sender, PhotoResult e) {
if (e.TaskResult == TaskResult.OK) {
BitmapImage image = new BitmapImage();
image.SetSource(e.ChosenPhoto);
WriteableBitmap wbm = new WriteableBitmap(image);
image.UriSource = null;
image = null;
wbm = null;
GC.Collect();
}
}
MainPage.xaml
<phone:PhoneApplicationPage.ApplicationBar>
<shell:ApplicationBar IsVisible="True" IsMenuEnabled="True" Mode="Default" Opacity="0.5" >
<shell:ApplicationBar.Buttons>
<shell:ApplicationBarIconButton IconUri="/junkUrl.png" Text="albums" Click="ApplicationBarIconButton_Click" />
</shell:ApplicationBar.Buttons>
</shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>

For this you have to store this file stream inside the IsolatedStorege. So create a filestream using IsolatedStorageFileStream and then save it, like this...
private void photoChooserTaskComplete(object sender, PhotoResult e) {
if (e.TaskResult == TaskResult.OK) {
SaveToIsolatedStorage(e.ChosenPhoto,"Your File Name");
}
}
public void SaveToIsolatedStorage(Stream imageStream, string fileName)
{
try
{
string imagename = fileName + ".jpg";
using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
if (myIsolatedStorage.FileExists(imagename))
{
myIsolatedStorage.DeleteFile(imagename);
}
IsolatedStorageFileStream fileStream = myIsolatedStorage.CreateFile(imagename);
WriteableBitmap wb = new WriteableBitmap(100, 100);
wb.SetSource(imageStream);
wb.SaveJpeg(fileStream, 100, 100, 0, 70);
fileStream.Close();
}
}
catch (Exception)
{
RadMessageBox.Show(String.Empty, MessageBoxButtons.OK, "Error occured while saving Images");
}
}
And for reading you can get that file from IsolatedStorage
public WriteableBitmap ReadFromIsolatedStorage(string fileName)
{
WriteableBitmap bitmap = new WriteableBitmap(100, 100);
try
{
using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
if (myIsolatedStorage.FileExists(fileName))
{
using (IsolatedStorageFileStream fileStream = myIsolatedStorage.OpenFile(fileName, FileMode.Open, FileAccess.Read))
{
// Decode the JPEG stream.
bitmap = PictureDecoder.DecodeJpeg(fileStream, 100, 100);
}
}
}
}
catch (Exception)
{
RadMessageBox.Show(String.Empty, MessageBoxButtons.OK, "Error Occcured while reading image");
}
return bitmap;
}
This will solved your memory leak problem, try this...

Related

How to set and save background image in windows phone?

I'm using this code, so user can sets custom background image for the application:
private void Button_Click(object sender, RoutedEventArgs e)
{
PhotoChooserTask photoChooserTask = new PhotoChooserTask();
photoChooserTask.Completed += new EventHandler<PhotoResult>(photoChooserTask_Completed);
photoChooserTask.Show();
}
void photoChooserTask_Completed(object sender, PhotoResult e)
{
if (e.TaskResult == TaskResult.OK)
{
System.Windows.Media.Imaging.BitmapImage bmp = new System.Windows.Media.Imaging.BitmapImage();
bmp.SetSource(e.ChosenPhoto);
var imageBrush = new ImageBrush
{
ImageSource = bmp,
Opacity = 0.5d
};
App.RootFrame.Background = imageBrush;
}
}
but this won't save background image for next application lunch.
now how can I save chosen photo to isolated storage to remains as app background even after restarting the application?
Save image asynchronously, applies to WP8 only.
public static async Task SaveImageAsync(string imageFileName, BitmapImage image)
{
// Get Students LocalFolder
IStorageFolder folder = await ApplicationData.Current.LocalFolder
.CreateFolderAsync("Images", CreationCollisionOption.OpenIfExists);
IStorageFile file = await folder.CreateFileAsync(
imageFileName, CreationCollisionOption.ReplaceExisting);
using (Stream stream = await file.OpenStreamForWriteAsync())
{
var wrBitmap = new WriteableBitmap(image);
wrBitmap.SaveJpeg(stream, image.PixelWidth, image.PixelHeight, 100, 100);
}
}
Read image synchronously both WP7.x WP8:
public static BitmapImage LoadImage(string imageFileName)
{
BitmapImage bitmapImage = null;
using (var isoFile = IsolatedStorageFile.GetUserStoreForApplication())
{
using (var isoStream = isoFile.OpenFile(
imageFileName, FileMode.Open, FileAccess.Read))
{
bitmapImage = new BitmapImage();
bitmapImage.SetSource(isoStream);
}
}
return bitmapImage;
}
You can find a bunch of resources online, just google it.
http://msdn.microsoft.com/en-us/library/xf96a1wz(v=vs.110).aspx
When Choosing
IsolatedStorageSettings.ApplicationSettings["backgroundImage"]=e.OriginalFileName;
On app loading
image.Source = new BitmapImage(new Uri(IsolatedStorageSettings.ApplicationSettings["backgroundImage"], UriKind.Absolute));
You can use the free EZ_Iso.dll to do this.
Just send your Bitmap off to the serializer with a name and let it handle the rest
//Saving
EZ_Iso.IsolatedStorageAccess.SaveImage(“MyImage”, YourImage);
//Retrieving
ImageControl.Source = EZ_Iso.IsolatedStroageAccess.GetImage(“MyImage”,Width,Height);
EZ_Iso.dll Download and Documentation

System.InvalidOperationException occur when load saved Image from IsolatedStorage

I have found a class from the link ImageCaching
but when i load from isolated storage i got an exeption "System.InvalidOperationException"
here is my code
public static object DownloadFromWeb(Uri imageFileUri)
{
WebClient m_webClient = new WebClient(); //Load from internet
BitmapImage bm = new BitmapImage();
m_webClient.OpenReadCompleted += (o, e) =>
{
if (e.Error != null || e.Cancelled) return;
WriteToIsolatedStorage(IsolatedStorageFile.GetUserStoreForApplication(), e.Result, GetFileNameInIsolatedStorage(imageFileUri));
bm.SetSource(e.Result);
e.Result.Close();
};
m_webClient.OpenReadAsync(imageFileUri);
return bm;
}
public static object ExtractFromLocalStorage(Uri imageFileUri)
{
byte[] data;
string isolatedStoragePath = GetFileNameInIsolatedStorage(imageFileUri); //Load from local storage
if (null == _storage)
{
_storage = IsolatedStorageFile.GetUserStoreForApplication();
}
using (IsolatedStorageFileStream sourceFile = _storage.OpenFile(isolatedStoragePath, FileMode.Open, FileAccess.Read))
{
// Read the entire file and then close it
sourceFile.Read(data, 0, data.Length);
sourceFile.Close();
BitmapImage bm = new BitmapImage();
bm.SetSource(sourceFile);///here got the exeption
return bm;
}
}
so that i can't set the image.
I'm using the converter you mentioned and it works but you modified the method.
Your ExtractFromLocalStorage method isn't the same. You close your stream before to use it with the SetSource method.
Here is the original method code:
private static object ExtractFromLocalStorage(Uri imageFileUri)
{
string isolatedStoragePath = GetFileNameInIsolatedStorage(imageFileUri); //Load from local storage
using (var sourceFile = _storage.OpenFile(isolatedStoragePath, FileMode.Open, FileAccess.Read))
{
BitmapImage bm = new BitmapImage();
bm.SetSource(sourceFile);
return bm;
}
}

Windows Phone - Image cache - wait for download an image

I made a project to Cache Images. I want to wait in main thread for complete DownloadImage function and then return that saved bitmap. Is that possible?
Am I doing it even properly?
public static ImageSource GetImage(int id)
{
BitmapImage bitmap = new BitmapImage();
String fileName=string.Format("ImageCache/{0}.jpg", id);
using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
if (!myIsolatedStorage.DirectoryExists("ImageCache"))
{
myIsolatedStorage.CreateDirectory("ImageCache");
}
if (myIsolatedStorage.FileExists(fileName))
{
using (IsolatedStorageFileStream fileStream = myIsolatedStorage.OpenFile(fileName, FileMode.Open, FileAccess.Read))
{
bitmap.SetSource(fileStream);
}
}
else
{
DownloadImage(id);
//HERE - how to wait for end of DownloadImage and then do that below??
using (IsolatedStorageFileStream fileStream = myIsolatedStorage.OpenFile(fileName, FileMode.Open, FileAccess.Read))
{
bitmap.SetSource(fileStream);
}
}
}
return bitmap;
}
Here is DownloadImage function:
private static void DownloadImage(Object id)
{
WebClient client = new WebClient();
client.OpenReadCompleted += new OpenReadCompletedEventHandler(client_OpenReadCompleted);
client.OpenReadAsync(new Uri(string.Format("http://example.com/{0}.jpg", id)), id);
}
private static void client_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
if (e.Error == null && !e.Cancelled)
{
try
{
string fileName = string.Format("ImageCache/{0}.jpg", e.UserState);
IsolatedStorageFileStream fileStream = myIsolatedStorage.CreateFile(fileName);
BitmapImage image = new BitmapImage();
image.SetSource(e.Result);
WriteableBitmap wb = new WriteableBitmap(image);
// Encode WriteableBitmap object to a JPEG stream.
Extensions.SaveJpeg(wb, fileStream, wb.PixelWidth, wb.PixelHeight, 0, 85);
fileStream.Close();
}
catch (Exception ex)
{
//Exception handle appropriately for your app
}
}
}
}
You have many way to achieve what you want. It is possible to wait with async await command which is part of Visual Studio Async.
You can download the latest CTP from here. More how to use it.
Personally I would use events.
This one contains some details and code example: http://www.ben.geek.nz/2010/07/one-time-cached-images-in-windows-phone-7/

How to save a picture from PhotoChooserTask

I would like to save a photo selected with the PhotoChooserTask, but I am unsure of the proper method. So far, I have implemented the PhotoChooserTask_Completed method, but what is the proper method to save this to a bitmap?
EDIT: added basic implementation. The goal is to update a hubtile image to an image that the user selects from the PhotoChooserTask.
Note: I have placed the Settings class and TileItem class at the bottom for quick reference.
MainPage.xaml
string shareJPEG = "shareImage.jpg";
string linkJPEG = "linkImage.jpg";
BitmapImage shareImg;
BitmapImage linkImg;
public MainPage()
{
InitializeComponent();
CreateHubTiles();
photoChooserTask = new PhotoChooserTask();
photoChooserTask.Completed += new EventHandler<PhotoResult>(photoChooserTask_Completed);
}
public void CreateHubTiles()
{
if (Settings.shareImageUpdated.Value == true)
{
//Settings.shareImage.Value = new BitmapImage();
shareImg = new BitmapImage();
using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream fileStream = myIsolatedStorage.OpenFile(shareJPEG, FileMode.Open, FileAccess.Read))
{
//Settings.shareImage.Value.SetSource(fileStream);
shareImg.SetSource(fileStream);
//this.img.Height = bi.PixelHeight;
//this.img.Width = bi.PixelWidth;
}
}
//this.img.Source = bi;
}
else
{
//Settings.shareImage.Value = new BitmapImage(new Uri("/Images/shareStatusImage.jpg", UriKind.Relative));
shareImg = new BitmapImage(new Uri("/Images/shareStatusImage.jpg", UriKind.Relative));
}
if (Settings.linkImageUpdated.Value == true)
{
//Settings.linkImage.Value = new BitmapImage();
linkImg = new BitmapImage();
using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream fileStream = myIsolatedStorage.OpenFile(linkJPEG, FileMode.Open, FileAccess.Read))
{
//Settings.linkImage.Value.SetSource(fileStream);
linkImg.SetSource(fileStream);
//this.img.Height = bi.PixelHeight;
//this.img.Width = bi.PixelWidth;
}
}
//this.img.Source = bi;
}
else
{
//Settings.linkImage.Value = new BitmapImage(new Uri("/Images/shareStatusImage.jpg", UriKind.Relative));
linkImg = new BitmapImage(new Uri("/Images/shareLinkImage.jpg", UriKind.Relative));
}
List<TileItem> tileItems = new List<TileItem>()
{
//new TileItem() { ImageUri = Settings.shareImage.Value, Title = "status", /*Notification = "last shared link uri",*/ Message = Settings.statusMessage.Value, GroupTag = "TileGroup", TileName = AppResource.Main_MainPage_HubTile_Status_Title },
new TileItem() { ImageUri = shareImg, Title = "status", /*Notification = "last shared link uri",*/ Message = Settings.statusMessage.Value, GroupTag = "TileGroup", TileName = AppResource.Main_MainPage_HubTile_Status_Title },
//new TileItem() { ImageUri = Settings.linkImage.Value, Title = "link", /*Notification = "last shared status message",*/ Message = Settings.linkUri.Value, GroupTag = "TileGroup", TileName = AppResource.Main_MainPage_HubTile_Link_Title },
new TileItem() { ImageUri = linkImg, Title = "link", /*Notification = "last shared status message",*/ Message = Settings.linkUri.Value, GroupTag = "TileGroup", TileName = AppResource.Main_MainPage_HubTile_Link_Title },
};
this.tileList.ItemsSource = tileItems;
}
public void changePictureMenuItem_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
var menuItem = (MenuItem)sender;
tileItem = menuItem.DataContext as TileItem; //for PhotoChooserTask_Completed
try
{
photoChooserTask.Show();
}
catch (System.InvalidOperationException ex)
{
//MessageBox.Show("An error occurred");
MessageBox.Show(AppResource.Main_MainPage_ContextMenu_ChangePicture_Error_Message);
}
}
void photoChooserTask_Completed(object sender, PhotoResult e)
{
//Debug.WriteLine("***\t In photoChooserTask_Completed function of ChoosePhotoPage\t ***");
if (e.TaskResult == TaskResult.OK)
{
//get the correct hubtile that was clicked and set image source to respective hubtile
string tileTitle = tileItem.Title.ToString();
switch (tileTitle)
{
case "status":
tileItem.ImageUri.SetSource(e.ChosenPhoto); //sets the tile image immediately, but does not persist when the MainPage is navigated away
using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
if (myIsolatedStorage.FileExists(shareJPEG))
{
myIsolatedStorage.DeleteFile(shareJPEG);
}
IsolatedStorageFileStream fileStream = myIsolatedStorage.CreateFile(shareJPEG);
BitmapImage bitmap = new BitmapImage();
bitmap.SetSource(e.ChosenPhoto);
WriteableBitmap wb = new WriteableBitmap(bitmap);
// Encode WriteableBitmap object to a JPEG stream.
Extensions.SaveJpeg(wb, fileStream, wb.PixelWidth, wb.PixelHeight, 0, 85);
//wb.SaveJpeg(fileStream, wb.PixelWidth, wb.PixelHeight, 0, 85);
fileStream.Close();
}
Settings.shareImageUpdated.Value = true; //simple boolean value that exists in isolated storage
break;
case "link":
tileItem.ImageUri.SetSource(e.ChosenPhoto); //sets the tile image immediately, but does not persist when the MainPage is navigated away
using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
if (myIsolatedStorage.FileExists(linkJPEG))
{
myIsolatedStorage.DeleteFile(linkJPEG);
}
IsolatedStorageFileStream fileStream = myIsolatedStorage.CreateFile(linkJPEG);
BitmapImage bitmap = new BitmapImage();
bitmap.SetSource(e.ChosenPhoto);
WriteableBitmap wb = new WriteableBitmap(bitmap);
// Encode WriteableBitmap object to a JPEG stream.
Extensions.SaveJpeg(wb, fileStream, wb.PixelWidth, wb.PixelHeight, 0, 85);
//wb.SaveJpeg(fileStream, wb.PixelWidth, wb.PixelHeight, 0, 85);
fileStream.Close();
}
Settings.linkImageUpdated.Value = true;
break;
}
}
The Settings and TileItem classes:
Settings.cs (uses key/value pairs to store data in isolated storage)
public static readonly Setting<BitmapImage> shareImage = new Setting<BitmapImage>("shareImage", new BitmapImage(new Uri("/Images/shareStatusImage.jpg", UriKind.Relative)));
public static readonly Setting<BitmapImage> linkImage = new Setting<BitmapImage>("linkImage", new BitmapImage(new Uri("/Images/shareLinkImage.jpg", UriKind.Relative)));
public static readonly Setting<bool> shareImageUpdated = new Setting<bool>("shareImageUpdated", false);
public static readonly Setting<bool> linkImageUpdated = new Setting<bool>("linkImageUpdated", false);
TileItem.cs
public class TileItem
{
//public string ImageUri
//{
// get;
// set;
//}
public BitmapImage ImageUri
{
get;
set;
}
public string Title
{
get;
set;
}
public string Notification
{
get;
set;
}
public bool DisplayNotification
{
get
{
return !string.IsNullOrEmpty(this.Notification);
}
}
public string Message
{
get;
set;
}
public string GroupTag
{
get;
set;
}
//for translation purposes (bound to HubTile Title on MainPage)
public string TileName
{
get;
set;
}
}
I do not get any debugging errors when running this application, but it seems thta the bitmaps are not being either saved or retrieved correctly, becuase the original tile image is the only one that persists when the app leaves the MainPage. What am I doing wrong here, and how may I fix this?
Here is the working code for storing image in Isolated storage and Loading it back. Check it.
string tempJPEG = "image.jpg";
void photoTask_Completed(object sender, PhotoResult e)
{
using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
if (myIsolatedStorage.FileExists(tempJPEG))
{
myIsolatedStorage.DeleteFile(tempJPEG);
}
IsolatedStorageFileStream fileStream = myIsolatedStorage.CreateFile(tempJPEG);
BitmapImage bitmap = new BitmapImage();
bitmap.SetSource(e.ChosenPhoto);
WriteableBitmap wb = new WriteableBitmap(bitmap);
// Encode WriteableBitmap object to a JPEG stream.
Extensions.SaveJpeg(wb, fileStream, wb.PixelWidth, wb.PixelHeight, 0, 85);
//wb.SaveJpeg(fileStream, wb.PixelWidth, wb.PixelHeight, 0, 85);
fileStream.Close();
}
}
//Code to load image from IsolatedStorage anywhere in your app
private void Button_Click_1(object sender, RoutedEventArgs e)
{
BitmapImage bi = new BitmapImage();
using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream fileStream = myIsolatedStorage.OpenFile(tempJPEG, FileMode.Open, FileAccess.Read))
{
bi.SetSource(fileStream);
this.img.Height = bi.PixelHeight;
this.img.Width = bi.PixelWidth;
}
}
this.img.Source = bi;
}

How to copy the selected image from picture library to the images folder dynamically in the application?

I am new to the windows phone 7 application development. I am accessing the picture library by using the PhotoChooserTask class. After selecting one of the picture from picture library I want to add that image (.jpg file) from picture library to the images folder of my application. How to do this ? I am using the following code
public partial class MainPage : PhoneApplicationPage
{
PhotoChooserTask photoChooserTask;
// Constructor
public MainPage()
{
InitializeComponent();
photoChooserTask = new PhotoChooserTask();
photoChooserTask.Completed += new EventHandler<PhotoResult>(photoChooserTask_Completed);
}
private void button1_Click(object sender, RoutedEventArgs e)
{
photoChooserTask.Show();
}
void photoChooserTask_Completed(object sender, PhotoResult e)
{
if (e.TaskResult == TaskResult.OK)
{
BitmapImage bmp = new BitmapImage();
bmp.SetSource(e.ChosenPhoto);
}
}
}
I want to add the the selected image dynamically to images folder of my application. how to do this? Can you please provide me any code or link through which I can resolve the above issue ?
Here's an example of saving the selected picture to IsolatedStorage and then reading it out to display it on the page:
void photoChooserTask_Completed(object sender, PhotoResult e)
{
if (e.TaskResult == TaskResult.OK)
{
var contents = new byte[1024];
using (var store = IsolatedStorageFile.GetUserStoreForApplication())
{
using (var local = new IsolatedStorageFileStream("image.jpg", FileMode.Create, store))
{
int bytes;
while ((bytes = e.ChosenPhoto.Read(contents, 0, contents.Length)) > 0)
{
local.Write(contents, 0, bytes);
}
}
// Read the saved image back out
var fileStream = store.OpenFile("image.jpg", FileMode.Open, FileAccess.Read);
var imageAsBitmap = PictureDecoder.DecodeJpeg(fileStream);
// Display the read image in a control on the page called 'MyImage'
MyImage.Source = imageAsBitmap;
}
}
}
You can refer to the RTM release notes on the revised Photo Chooser API or the doco a bit further down the page here.
How to: Create a Photo Extras Application for Windows Phone
In fact, once you obtain the stream, you can convert it to byte and store locally. Here is what you should have in your Task_Completed event handler:
using (MemoryStream stream = new MemoryStream())
{
byte[] contents = new byte[1024];
int bytes;
while ((bytes = e.ChosenPhoto.Read(contents, 0, contents.Length)) > 0)
{
stream.Write(contents, 0, bytes);
}
using (var local = new IsolatedStorageFileStream("myImage.jpg", FileMode.Create, IsolatedStorageFile.GetUserStoreForApplication()))
{
local.Write(stream.GetBuffer(), 0, stream.GetBuffer().Length);
}
}

Categories