Access Denied when deleting image file previously used in DataTemplate in WinRT - xaml

I have image (PNG) files used by my GridView as part of its DataTemplate. If I try to delete a specific object row in my GridView, I would delete the related image file for that row as well. The images are different for every item on the list.
I'm using this code to delete the image file
StorageFile _file = await DataStore.GetFileAsync(filename);
await _file.DeleteAsync(StorageDeleteOption.Default);
The image file is rendered on the GridView under the GridView's DataTemplate.
So in each object model in my List, I have a public property there that returns an ImageSource for my DataTemplate.
I'm calling my delete procedure right after i deleted the object row from the List and after the GridView has been refreshed of the new List items.
Even though the List does not contain the object (consuming the image) anymore, the app throws the Access is Denied exception if i try to delete the file. While the app is running, if i try to delete that particular file manually (through file explorer), it won't allow me too.
I tried clearing all unused objects in my app, even setting the GridView's ItemSource to null and the List to null before I delete the image. Still the exception persist.
Thanks in advance.

One method you can try is to load the image into a memory steam, then create a BitmapImage object from that stream, you can then set the source of your Image control to that bitmap image.
Since you are not using the actual image file as the source of the image, you can easily delete it anytime :)

Though this is an old question, I have encountered the problem recently in a UWP app and actually managed to find a solution. But first some background information about the problem:
When you create a BitmapImage with a URI, the created URI object holds a reference to the file in your local storage and keeps it open, i.e. non-writable. This btw is only true when the Bitmap is just big enough to fit into the Image entirely, typically small or medium sized Bitmaps. If the Bitmap happens to be big enough, WinRT automatically uses a downsampled version when it is displayed in an Image. In this case the URI does NOT hold a reference to the original file.
Now to the actual solution:
Setting Image.Source to null doesn't do the trick here, as the URI is still alive (until the next GC cycle at least). What DID work for me, was casting the Source to the BitmapImage it originally was and settings the UriSource to null.
var bitmapImage = image.Source as BitmapImage;
if (bitmapImage != null)
bitmapImage.UriSource = null;
And yes, this IS stupid.

Mark already mentioned part of this but there is no need to make it that complicated. Simply, wherever you expect that you need to delete the bitmap while the system is holding it, use a converter like this:
public class SafeImageFileConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
string path = (string)value;
var stream = new MemoryStream(File.ReadAllBytes(path));
return ImageSource.FromStream(() => stream);
}
}
In practice, you might want to check whether the path exists or return an error.png or similar if it doesn't. Also, don't be tempted to use using with the stream, the system will need the stream so you shouldn't dispose it early.

The trick is to use a Uri object to load the image (instead of a string filename) and then to use the EXACT same Uri instance to delete the file (after removing the image from the UI, of course). Here is an example:
//Save the Uri as a member variable so you can get to it later
private Uri uri;
//Create the Uri
uri = new Uri(OriginalImageFilename, UriKind.Absolute);
//Load the image
BitmapImage bitmapImage = new BitmapImage(uri);
//This can also be done by binding a Image control's source property to the uri.
//Delete the image (remember to use the same Uri instance)
StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(uri);
await file.DeleteAsync();

As a work-around, i just deleted the unused images during app launch so that no processes are using it. Thanks.

Related

Why cant I databind a StreamImageSource in XAML and how to bind an Image properly?

<Image Source="{Binding s, Mode=OneWay}"></Image>
with a StreamImageSource-Property:
public ImageSource s { get; set; }
Throws following exception:
Failed to create image decoder with message 'unimplemented' [0:]
ImageLoaderSourceHandler: Image data was invalid:
Xamarin.Forms.StreamImageSource
I wonder why this happens? I guess I have to create a Converter, but I dont know what I am supposed to convert? Which argument is correct for Image.source ?
If you put the image to each platform,you could use ImageSource = ImageSource.FromFile("xxx.png").
If you put it in the forms project and set it Build Action as EmbeddedResource,you could use ImageSource.FromResource(Source, typeof(your class).GetTypeInfo().Assembly)
You could look at https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/images?tabs=windows#local-images.
And here is an answer which have achieve the two methods above.You could refer to it.
https://stackoverflow.com/a/56315412/10768653
It seems like Streams get disposed and closed as soon as one uses them to create a source. Thats why the Image data was "invalid", because there was no image to read from a closed stream. My solution was to create a copy from the stream everytime I use it.
ImageSource.FromStream(() => new MemoryStream(stream.ToArray()));

Converter failed to convert value of type 'Windows.Foundation.String' to type 'ImageSource'

This is for my Windows 8 app:
In my object I have a string property that contains the path of the images I want to use.
public String ImagePath
In my XAML I have set up an Image tag with the following binding:
<Image Source="{Binding ImagePath}" Margin="50"/>
When I reference an image that I've included in my project (in the Asset folder) the image displays correctly. The path is: Assets/car2.png
However, when I reference an image that the user selects (using the FilePicker) I get an error (and no image). The path is: C:\Users\Jeff\Pictures\myImage.PNG
Converter failed to convert value of type 'Windows.Foundation.String'
to type 'ImageSource'
Just to add a little more info. When I use the file picker I am converting the file location to a URI:
Uri uriAddress = new Uri(file.Path.ToString());
_VM.vehicleSingle.ImagePath = uriAddress.LocalPath;
Update:
I'm also saving this image path to isolated storage. I think this is where the issue is. I'm able to save the path of the file selected, but when I try to bind to it when I'm reloading the Isolated Storage it doesn't work.
So if I can't use an image outside of the application directory. Is there a way I can save that image and add it to the directory?
I tried creating a BitmapImage property to my model but now I'm getting errors stating that it can't serialize a BitmapImage.
You should Use Converter
public class ImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
MemoryStream memStream = new MemoryStream((byte[])value,false);
BitmapImage empImage = new BitmapImage();
empImage.SetSource(memStream);
return empImage;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
You can't use a file path that points outside the app directory. You will need to read in the StorageFile stream that you get from the file picker and assign that stream to an image source - so binding is pretty hard unless you change your model,to have an imagesource property instead.
As mentioned, you cannot use bindings to access the file system directly, even if you grant access via the File Picker. Take a look at the XAML Images Sample at the Dev Center, for a technique you can use.
In a nutshell, you'll use SetSourceAsync to get your file into a BitmapImage and then you can use that as the binding source.
I recently did some work on binding to an ImageSource.
public System.Windows.Media.ImageSource PhotoImageSource
{
get
{
if (Photo != null)
{
System.Windows.Media.Imaging.BitmapImage image = new System.Windows.Media.Imaging.BitmapImage();
image.BeginInit();
image.StreamSource = new MemoryStream(Photo);
image.EndInit();
return image as System.Windows.Media.ImageSource;
}
else
{
return null;
}
}
}
My "Photo" was an image stored in a byte[]. You could either convert your image to a byte[] or maybe try using a FileStream instead (I haven't tested with a FileStream so I can't say if it will work).

How to give the option to change the Textbloack foreground-color,size in Windowsphone7

I am completely new in Windowsphone7.i have develop sample application in that i want give the option to change the font-color,Size,style(Italic/Bold) as Dynamically(like RadEditor) .please help me how to resolve this option.
If you Develop your Application MVVM style then it is not so hard to do this. You just need a property for every setting you want to set dynamically and then bind to this properties. And you create a Settings View where you can set the properties and if you change them you use INotifyPropertyChanged to broadcast that your properties value changed and so every control which is bound to that property will change and redraw.
GalaSoft MVVM
MVVM Codeplex
Easy MVVM sample Application for Windows Phone 7
The link you found to save an image looks ok, but i did it a bit different, actually from the CameraCaptureTask you already get a WritableBitmap Image and you can save it like this.
To save the Image:
private void SaveToIsolatedStorage(WriteableBitmap image, string fileName)
{
using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
if (myIsolatedStorage.FileExists(fileName))
{
myIsolatedStorage.DeleteFile(fileName);
}
using (var stream = myIsolatedStorage.OpenFile(fileName, FileMode.Create))
{
Extensions.SaveJpeg(image, stream, image.PixelWidth, image.PixelHeight,0, 100);
}
}
}
To read the Image:
private WritableBitmap ReadFromIsolatedStorage(string fileName)
{
WriteableBitmap bitmap = new WriteableBitmap(200,200);
using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
if (store.FileExists(fileName))
{
using (var stream = store.OpenFile(fileName,FileMode.Open))
{
bitmap.SetSource(stream);
}}
}
return bitmap;
}
I hope this will work because i wrote it from scratch. :)
And in your ViewModel you should have a WritableBitmap Property which is bound to your Image Control on your View.
To use a lot of images and work with them you should read a bit more about this, because somehow SL Images use a lot of memory so you will need to address this problem somehow in the future.

Adding content to silver light controls from database using wcf ria services

I am trying to retrieve strings from the database and add it as content to the controls on my page before it loads(somewhat like a custom localization). I retrieve my strings from the database uing ria services as follows:
**
Web.DomainService1 context = new Web.DomainService1();
LoadOperation<Web.LocalizationTab>LoadOp=context.Load(context.GetLocalizationTabsQuery(currentCulture, moduleName));
Dictionary<string, string> localizationDictonary = new Dictionary<string, string>();
List<Web.LocalizationTab> localList = new List<Web.LocalizationTab>();
LoadOp.Completed += (s, e) =>
{
localList = LoadOp.Entities.ToList<Web.LocalizationTab>();
//System.Windows.MessageBox.Show(localList.Count.ToString());
foreach (Web.LocalizationTab item in localList)
{
// var control = this.FindName(item.Control_ID.ToString());
if (!localizationDictonary.ContainsKey(item.Control_ID))
{
localizationDictonary.Add(item.Control_ID, item.Control_Text);
}
}
};**
This piece of code is in a separate class called utilities.cs.
now in my Mainpage.xaml.cs i need to get this dictionary with values and then set the controls with the strings from the dictionary.
my problem is that when i do the following in the constructor of Mainpage.xaml.cs:
utilities.getDict(ModuleName);
button1.Content = localizationDictonary["button1"].ToString();
i get an exception as the dictionary doesnt contain values at that point of time.. The load completed event of getting data from the database gets fired only after my constructor is exited. now how do i go about setting my controls automatically in this particular scenario??
Try loading your dictionary in the Silverlight Application class StartUp event. This should ensure that your Dictionary is loaded (and could be placed in the Application Resources for retrieval elsewhere in your application).
You'll need to wait for the GetLocalizationTabsQuery to complete before instantiating your MainPage. In your App.xaml.cs file, you can start the query, add a query completed handler, and only create the MainPage when the query completed handler gets called.

Out of memory exception while loading images

I am using the following piece of code to load images as thumbnails to a FlowLayoutPanel control. Unfortunately i get an OutOfMemory exception.
As you already guess the memory leak is found at line
Pedit.Image = System.Drawing.Image.FromStream(fs)
So how could i optimize the following code?
Private Sub LoadImagesCommon(ByVal FlowPanel As FlowLayoutPanel, ByVal fi As FileInfo)
Pedit = New DevExpress.XtraEditors.PictureEdit
Pedit.Width = txtIconsWidth.EditValue
Pedit.Height = Pedit.Width / (4 / 3)
Dim fs As System.IO.FileStream
fs = New System.IO.FileStream(fi.FullName, IO.FileMode.Open, IO.FileAccess.Read)
Pedit.Image = System.Drawing.Image.FromStream(fs)
fs.Close()
fs.Dispose()
Pedit.Properties.SizeMode = DevExpress.XtraEditors.Controls.PictureSizeMode.Zoom
If FlowPanel Is flowR Then
AddHandler Pedit.MouseClick, AddressOf Pedit_MouseClick
AddHandler Pedit.MouseEnter, AddressOf Pedit_MouseEnter
AddHandler Pedit.MouseLeave, AddressOf Pedit_MouseLeave
End If
FlowPanel.Controls.Add(Pedit)
End Sub
Update: The problem occurs while loading a number of images (3264x2448px at 300dpi - each image is about 3Mb's)
Documentation for Image.FromFile (which is related to your FromStream) says that it will throw OutOfMemoryException if the file is not a valid image format or if GDI+ doesn't support the pixel format. Is it possible you're trying to load an unsupported image type?
Also, documentation for Image.FromStream says that you have to keep the stream open for the lifetime of the image, so even if your code loaded the image you'd probably get an error because you're closing the file while the image is still active. See http://msdn.microsoft.com/en-us/library/93z9ee4x.aspx.
Couple of thoughts:
First off, as Jim has stated, when using Image.FromStream the stream should remain open for the lifetime of the Image as remarked on the MSDN page. As such, I would suggest to copy the contents of the file to a MemoryStream, and use the latter to create the Image instance. So you can release the file handle asap.
Secondly, the images you're using are rather big (uncompressed, as they would exist in memory, Width x Height x BytesPerPixel). Assuming the context you use them in might allow for them to be smaller, consider resizing them, and potentially caching the resized versions somewhere for later use.
Lastly, don't forget to Dispose the image and the Stream when they are no longer needed.
You can solve this in a few steps:
to get free from the File-dependency, you have to copy the images. By really drawing it to a new Bitmap, you can't just copy it.
since you want thumbnails, and your source-bitmaps are rather large, combine this with shrinking the images.
I had the same problem. Jim Mischel answer led me to discover loading an innocent .txt file was the culprit. Here's my method in case anyone is interested.
Here's my method:
/// <summary>
/// Loads every image from the folder specified as param.
/// </summary>
/// <param name="pDirectory">Path to the directory from which you want to load images.
/// NOTE: this method will throws exceptions if the argument causes
/// <code>Directory.GetFiles(path)</code> to throw an exception.</param>
/// <returns>An ImageList, if no files are found, it'll be empty (not null).</returns>
public static ImageList InitImageListFromDirectory(string pDirectory)
{
ImageList imageList = new ImageList();
foreach (string f in System.IO.Directory.GetFiles(pDirectory))
{
try
{
Image img = Image.FromFile(f);
imageList.Images.Add(img);
}
catch
{
// Out of Memory Exceptions are thrown in Image.FromFile if you pass in a non-image file.
}
}
return imageList;
}