I have built a digital signage solution running in WPF. I have an executable that handles scheduling and a number of external assemblies (.dll with user controls) that is responsible of displaying an image, a video etc.
When the application starts it loads all the assemblies into a list and then uses an XML file with configuration info on which kind of slide to start with which parameters, like this:
<signage>
<slide type="image" argument="c:\image1.png"/>
<slide type="image" argument="c:\image2.png"/>
<slide type="video" argument="c:\video1.mpg"/>
...
</signage>
That list can be very long and contain many different slides. When i build my list of objects to show i create a new instance, via reflection, of the .dll that i need to show, passing it the argument. This is contained within a list. The parent executable is then traversing the list and displaying the user control instances (the instantiated slide types) on my main WPF application.
Thats the background for my question.
When i need to update the contents on runtime i need to replace the files on disk (image1, image2 etc...) but get an exception that the files are in use by another process and that i cannot access them.
Any idea on how to fix this? Is there any way i can "unload" them from my application? Is there any way to do this in a proper way?
EDIT
Here is some additional info on how i do this in an image assembly:
public ImageExtensionControl(XmlDocument document, Dictionary<string, string> settings, Action slideFinished)
{
InitializeComponent();
string path = new FileInfo(Assembly.GetCallingAssembly().Location).Directory.FullName;
string id = settings["Image"];
path = System.IO.Path.Combine(path, document.SelectSingleNode("properties/files/file[#id='" + id + "']").Attributes["path"].Value);
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.UriSource = new Uri(path, UriKind.Absolute);
bitmapImage.EndInit();
this.myimage.Source = bitmapImage;
}
If you're loading your images in, ensure you are closing the stream once you're finished with your operation. For instance,
using(var fs = new FileStream(...))
{
// ...
} // <-- Stream is closed and disposed.
Possibly try caching options:
Problems overwriting (re-saving) image when it was set as image source
imgTemp = new BitmapImage();
imgTemp.BeginInit();
imgTemp.CacheOption = BitmapCacheOption.OnLoad;
imgTemp.CreateOption = BitmapCreateOptions.IgnoreImageCache;
imgTemp.UriSource = uriSource;
imgTemp.EndInit();
imgAsset.Source = imgTemp;
Related
As a selfteaching project I am building a memory game as a WPF app.
The question: is there an efficient way of adding all the image files from Resources dirrectory to a List<BitmapImage> so that I can easiely create a grid of x amount of images?
The ideal for me would have been to store all those images once in a list and cycle through the list in order to set the (Image)pictureBox.Source = myImageList[i]
The constrains here are that the myImage.BeginInit() method can only be called once, and on top of that it is not possible to override an instance of a BitmapImage. (by "cannot override" I mean that I cannot change the picture held by the BitmapImage once it has been set.
Conclusion: I have a lot of pictures in my resource folder. Is there a better way than the following piece of code to save all my images from that folder in a list. (since this method requires me to create an instance of a BitmapImage for each resource I want to add to the list. It feels inefficient)
BitmapImage myImage0 = new BitmapImage();
myImage0.BeginInit();
myImage0.UriSource = new Uri(#"/resources/ukflag.jpg", UriKind.Relative);
pictureBox.Source = myImage0;
myImage0.EndInit();
myImageList.Add(myImage0);
Thanks to the comments I found a way.
here is the code that worked for me (after setting build action of the pictures to resource)
ResourceSet resourceSet = Properties.Resources.ResourceManager.GetResourceSet(CultureInfo.CurrentUICulture, true, true);
foreach (DictionaryEntry entry in resourceSet)
{
string resourceKey = entry.Key.ToString();
myImageList.Add(new BitmapImage(new Uri(#"pack://application:,,,/Resources/" + resourceKey + ".jpg")));
}
I use OpenFileDialog to get the path of an image and then set it to my image source property imgSelected.Source = new BitmapImage(new Uri(filenameSteg, UriKind.Absolute)); in a WPF application.
The thing is, I need to open that file again later but I can't open it since the file is being used by another process ( System.IO.IOException -> The process cannot access the file pathToFile because it is being used by another process.).
The code that needs to access it later it as follow :
bitmapSource = JpegBitmapDecoder.Create(File.Open(filenameSteg, FileMode.Open),
BitmapCreateOptions.None, BitmapCacheOption.OnLoad).Frames[0];
This bitmapSource is used to give that image to a WriteableBitmap and from there I go through the pixels.
Is there any way to dispose of a File opened with an OpenFileDialog ?
I tried to cast to to IDisposable, to use a using block and so on but this thing is persistent.
EDIT :
1 - I tried this (#ctacke answer) :
using (var stream = File.Open(filenameSteg, FileMode.Open)){
bitmapSource = JpegBitmapDecoder.Create(stream, BitmapCreateOptions.None,
BitmapCacheOption.OnLoad).Frames[0];}
But it still gives me the error about the process being already in used by another process because even though it will be disposed after, I still am trying to open the same file (filenameSteg) than I opened in the OpenFileDialog. (Or at least, that's how I see it.)
2 - I then tried this (based on #ctacke recommended link:
using (FileStream fileStream = new FileStream(filenameSteg+1, FileMode.Create)){
MemoryStream memoryStream = new MemoryStream();
BitmapImage bi = new BitmapImage();
byte[] fileBytes = File.ReadAllBytes(filenameSteg);
memoryStream.Write(fileBytes, 0, fileBytes.Length);
memoryStream.Position = 0;
bi.BeginInit();
bi.StreamSource = memoryStream;
bi.EndInit();
bitmapSource = bi;}
Note : Notice that I here ask for filenameSteg +1. That is because I wanted to test the rest of my method so I create a copy the file and simply added a 1 to its name. That being said, when using filenameSteg for real, it gave me the same error about being already in used which I again suspect is that I still am asking to open the same image that was previously opened in the OpenFileDialog.
3 - I thought of another approach which does not require me to dispose the opened image :
When I open the image for the first time in the OpenFileDialog, I store the byte array of the image in a variable so I could create the WriteableBitmap using the BitmapFactory and the byte array.
// This is in the OpenFileDialog. It is where I stock the image "pixels" in a byte array.
bytesArrayImage = File.ReadAllBytes(filenameSteg);
//And then later when I needed the WriteableBitmap, I used the byte array and the BitmapFactory
//imgSelected being the Image containing the image I opened in the OpenFileDialog, I used it's
//width and height
wb = BitmapFactory.New(Convert.ToInt32(imgSelected.Width),
Convert.ToInt32(imgSelected.Height)).FromByteArray(bytesArrayImage);
The problem with this approach is that, some pictures works fine and I can use the byte array to create the WriteableBitmap and go through it's pixels but in other cases it gives me an AccessViolationException stating : Attempted to read or write protected memory. This is often an indication that other memory is corrupt.. In other words, trying to bypass the dispose problem got me into another problem.
You should release the original image, something like this:
if(imgSelected.Source != null)
{
imgSelected.Source.Dispose;
}
imgSelected.Source = new BitmapImage(new Uri(filenameSteg, UriKind.Absolute));
Next, File.Open returns a stream, which you need to explicitly release.
using(var stream = File.Open(filenameSteg, FileMode.Open))
{
var bitmapSource = JpegBitmapDecoder.Create(stream,
BitmapCreateOptions.None, BitmapCacheOption.OnLoad).Frames[0];
}
See also: Load a BitmapSource and save using the same name in WPF -> IOException
I've taken over a poorly designed project from a co-worker and am looking to load a picture box directly from the WIA command that just completed to take a picture from an attached USB camera.
The current implementation waits until the file has been written to disk, then displays it from there probably
re-reading the file from disk.
Item item = d.ExecuteCommand(WIA.CommandID.wiaCommandTakePicture);
WIA.ImageFile imagefile = item.Transfer(FormatID.wiaFormatJPEG) as WIA.ImageFile;
I tried casting an Image to the given picture box without success
picBox.Image = (Image)imageFile;
WIA.ImageFile is a COM object wrapper, not a System.Drawing.Image.
You'll have to mess with WIA.ImageFile Save method with temporary files or try in-memory solution as shown here:
http://www.pcreview.co.uk/forums/wia-imagefile-system-drawing-image-t2321026.html
var imageBytes = (byte[])image.FileData.get_BinaryData();
var ms = new MemoryStream(imageBytes);
var img = Image.FromStream(ms);
Greetings all,
Thanks for reading and sharing any insights. I've created a new simple Windows Phone 7 application. I have two Image objects on the form I would like to update with various .png files. I have included the .png files into my project, and I believe I am building proper URi resources pointing to these files.
Uri myImage1URi = new Uri( strImage1, UriKind.RelativeOrAbsolute );
Uri myImage2URi = new Uri( strImage2, UriKind.RelativeOrAbsolute );
System.Windows.Media.Imaging.BitmapImage bi1 = new System.Windows.Media.Imaging.BitmapImage(myImage1URi);
System.Windows.Media.Imaging.BitmapImage bi2 = new System.Windows.Media.Imaging.BitmapImage(myImage2URi);
image1.Source = bi1;
image1.Stretch = Stretch.Fill;
image2.Source = bi2;
image2.Stretch = Stretch.Fill;
This alone is not accomplishing what I want (to update the images to the two from the URi's).
I know there is something a bit off going on (IE: I am doing something dumb) in that all of the BitmapImage class descriptions mention that I have to do a .BeginInit() before I work with the BitmapImage object, and a .EndInit() call afterwards. These method calls don't work for me, so I know something is amiss....
Or maybe I am competely off base and I simply need a way to tell my main window to repaint itself? That thought has occurred to me as well.
Thanks again.
The following will load an image that is in the appropriate path and is set as having a build action content.
myImg.Source = new BitmapImage(new Uri("/images/filename.png", UriKind.Relative));
It assumes XAML on the page like:
<Image x:Name="myImg" />
This seems very similar to what you're doing. Simplify what you're doing ot get it working.
Does it work with just using one image?
Is the path in strImageN a valid path?
Do the image files have their build action set to Content?
Good day all,
I am having some trouble with image permissions.
I am loading an image from file, resizing it and then saving it out to another folder.
I am then displaying this like so:
uriSource = new Uri(Combine(imagesDirectoryTemp, generatedFileName), UriKind.Absolute);
imgAsset.Source = new BitmapImage(uriSource);
This is working fine, the trouble comes if the user then selects another image immediately after and tries to save it over the original file.
An exception is generated upon saving my image "ExternalException: A generic error occurred in GDI+."
After some playing around i have narrowed the error down to imgAsset.Source = new BitmapImage(uriSource); as removing this line and not setting the imagesource will allow me to overwrite this file many times.
I have also tried setting the source to something else, before re-saving in the hope that the old reference would be disposed, this was not the case.
How can i get past this error?
Thanks,
Kohan
Edit
Now using this code i am not getting the exception however the image source is not updating. Also since i am not using a SourceStream, im not sure what i need to dispose of to get this working.
uriSource = new Uri(Combine(imagesDirectoryTemp, generatedFileName), UriKind.Absolute);
imgTemp = new BitmapImage();
imgTemp.BeginInit();
imgTemp.CacheOption = BitmapCacheOption.OnLoad;
imgTemp.UriSource = uriSource;
imgTemp.EndInit();
imgAsset.Source = imgTemp;
You're almost there.
Using BitmapCacheOption.OnLoad was the best solution to keep your file from being locked.
To cause it to reread the file every time you also need to add BitmapCreateOptions.IgnoreImageCache.
Adding one line to your code should do it:
imgTemp.CreateOption = BitmapCreateOptions.IgnoreImageCache;
thus resulting in this code:
uriSource = new Uri(Combine(imagesDirectoryTemp, generatedFileName), UriKind.Absolute);
imgTemp = new BitmapImage();
imgTemp.BeginInit();
imgTemp.CacheOption = BitmapCacheOption.OnLoad;
imgTemp.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
imgTemp.UriSource = uriSource;
imgTemp.EndInit();
imgAsset.Source = imgTemp;
When you load an Image in any WPF control it let's handle to your image and doesn't release it until you close your application. The reason for this... I dont know exactly, problably is relaying on some DirectX code behind the scene which never knows when the WPF application releases the image..
Use this code to load the image..
MemoryStream mstream = new MemoryStream();
System.Drawing.Bitmap bitmap = new Bitmap(imgName);
bitmap.Save(mstream, System.Drawing.Imaging.ImageFormat.Jpeg);
bitmap.Dispose(); // Releases the file.
mstream.Position = 0;
image.BeginInit();
image.StreamSource = mstream;
image.EndInit();
this.img.Source = image ;
it worked for me..
Sounds very much like the problem I had developing Intuipic, where WPF would not dispose of the image, thus locking the file. Check out this converter I wrote to deal with the problem.