Dynamic WPF Image Loading Issue - c#

I have an issue that I think has not been covered in the multitude of other WPF image loading issues. I am scanning in several images and passing them to a "Preview Page". The preview page takes the image thumbnails and displays what a printout would look like via a generated bitmap.
The weird thing to me is, it will work fine if I run the program the first time. Upon reaching the end of the process and hitting "start over", the preview will return blank. I am creating the BitmapImage in a method that saves the bitmap as a random file name so I do not believe theres a lock on the file the second time around. Also, if I go to look at the temporary file created through explorer, it is drawn correctly so I know the appropriate data is getting to it.
Finally, when I navigate away from this page, I am clearing necessary data. I'm really perplexed and any help would be appreciated.
//Constructor
public Receipt_Form() {
InitializeComponent();
printData = new List<Object>();
this.Loaded += new RoutedEventHandler(MainWindow_Loaded);
}
void MainWindow_Loaded(object sender, RoutedEventArgs e) {
// populates global variable fileName
var task = System.Threading.Tasks.Task.Factory.StartNew(() => outputToBitmap()); task.ContinueWith(t => setImage(fileName),
System.Threading.Tasks.TaskScheduler.FromCurrentSynchronizationContext());
// I started the image creation in a separate thread because I
// thought it may be blocking the UI thread, but it didn't matter
}
private void setImage(string imageURI) {
BitmapImage image;
using (FileStream stream = File.OpenRead(imageURI)) {
image = new BitmapImage();
image.BeginInit();
image.StreamSource = stream;
image.CacheOption = BitmapCacheOption.OnLoad;
image.EndInit();
}
receiptPreview.Source = image;
//this works the first iteration but not the second, though the temp file is created successfully
}

Found the issue - the Modern UI container was getting cleared when transitioning off the page.

Related

PictureBox assigned ErrorImage is not shown after incorrectly Disposing its Image

I have a winforms application where user can select an image from a list of available images and the corresponding image is shown in a PictureBox. The images can be very huge with a minimum of 10MB. This obviously makes the rest of the UI unresponsive while the image loads. So I thought of loading the image on a separate thread using the following code:
private void LoadImage()
{
// loadViewerThread is a Thread object
if (loadViewerThread != null && loadViewerThread.IsAlive)
{
loadViewerThread.Abort(); // Aborting the previous thread if the user has selected another image
}
loadViewerThread = new Thread(SetViewerImage);
loadViewerThread.Start();
}
The SetViewerImage function is as below:
private void SetViewerImage()
{
if (pictureBox1.Image != null)
pictureBox1.Image.Dispose();
pictureBox1.Image = new Bitmap(/*Some stream*/);
}
After this the images load smoothly and the UI can also be accessed.
But if the user moves very fast between the set of images then a big red X mark comes up. This happens because I have that call to Dispose in SetViewerImage.
I have assigned an ErrorImage to the PictureBox but that ErrorImage is never shown in this case.
Questions:
Is there anything wrong with my thread implementation? Why does the
Image gets disposed?
Is there any way that I can display a different ErrorImage and not
the red X?
You need to manipulate controls in the UI thread. You can do this by using Control.Invoke() from a different thread.
The big bottleneck is creating the image from the stream, so you should be able to reorganize your method like this to keep the UI thread freed up:
private void SetViewerImage()
{
Bitmap image = new Bitmap(/* Some stream*/);
pictureBox1.Invoke(new Action(() =>
{
if (pictureBox1.Image != null)
{
pictureBox1.Image.Dispose();
pictureBox1.Image = null; // This might help? Only add if it does.
}
pictureBox1.Image = image;
}));
}

WP8: C# app crashes while changing BitMapImage

Okay, I do not know what is causing the crash of my app and I simply do not understand what is happening. I will explain shortly what my app CAN do and what is my problem.
Furthermore I read barely any topic about that on here, and was clicking my way through different google sites.
I am not finding a solution, so I have to ask!
I have an image set as Background -> Works fine.
I have a TextBlock which displays different text every 15 seconds, controlled by a timer, each text is saved in a list! -> Works fine.
I have a fade in/fade out of that text -> Works fine.
I have an application bar at the bottom -> Works fine.
Chaing one explicit picture with that peace of code -> Works fine.
private void Appearance_Click(object sender, EventArgs e)
{
Hintergrund.Source = new BitmapImage(new Uri("/Pictures/StarsNight19.jpg", UriKind.Relative));
}
Well I have around 20 different Images, all have pretty the same names, saved in a folder in my project. The path is as shown in the code fragment: /Pictures/StarsNightXX.jpg
Build Action is set to: CONTENT (well tried everything basically..)
Copy To Output Directory is set to: Copy Always.
Now here is my problem.
I saved the names of my images in a List.
.....
pictures.Add("StarsNight4.jpg");
pictures.Add("StarsNight5.jpg");
pictures.Add("StarsNight6.jpg");
....
I use the same operation as before, wanting it to change the image when clicked on the nice little button in my application bar:
private void Appearance_Click(object sender, EventArgs e)
{
Random rnd = new Random();
int next = rnd.Next(0, pictures.Count - 1);
background.Source = new BitmapImage(new Uri("/Pictures/"+pictures.ElementAt(next), UriKind.RelativeOrAbsolute));
}
BOOM APP CRASHES
I just can not figure out where the problem is.
Changing it by writing an explicit name as shown at the beginning is working out fine...
Maybe someone can tell me if the list is causing a problem?
I just can not figure it out.
"looping it" does not work out either:
int i = 0;
private void Appearance_Click(object sender, EventArgs e)
{
if (i >= pictures.Count) i = 0;
background.Source = new BitmapImage(new Uri("/Pictures/" + pictures.ElementAt(i), UriKind.RelativeOrAbsolute));
i++;
}
Because I am testing my App directly on my WP I do not know what kind of exception I get.
No way compiling and testing it on my computer just to let you know.
...
Losing my mind right here.
Kindly try this source code, I've tried to dispose of the object which are created in the source below, use your list and other code such as loop as it is.
private static BitmapImage bi = null;//this line at the top, not in function
private static Image si = null;//this line at the top, not in function
if bi!=null)
{
bi.Dispose();
bi = null;
}
if si!=null)
{
si.Dispose();
si = null;
}
BitmapImage bi = new BitmapImage();
Image si = new Image();
bi.BeginInit();
bi.UriSource = new Uri(#"/img/img1.jpg",UriKind.RelativeOrAbsolute);
bi.EndInit();
si.Source = bi;

Showing animated gif in Winforms without locking the file

I'm trying to display images of various file types (including animated .gif files) in my Winforms application. I also have to be able to modify the files that are shown. (change the file name, delete them).
The problem is that a Picturebox locks the image file until the application is closed when using the normal way.
That means I can't do this:
private void Form1_Load(object sender, EventArgs e)
{
PictureBox pic = new PictureBox();
pic.Size = new Size(250, 250);
pic.Image = Image.FromFile("someImage.gif");
this.Controls.Add(pic);
//No use to call pic.Image = null or .Dispose of it
File.Delete("someImage.gif"); //throws exception
}
The workaround in the link above is as follows:
private void Form1_Load2(object sender, EventArgs e)
{
PictureBox pic = new PictureBox();
pic.Size = new Size(250, 250);
//using a FileStream
var fs = new System.IO.FileStream("someImage.gif", System.IO.FileMode.Open, System.IO.FileAccess.Read);
pic.Image = System.Drawing.Image.FromStream(fs);
fs.Close();
this.Controls.Add(pic);
pic.MouseClick += pic_MouseClick;
}
That works fine for normal image types, but it won't load animated .gifs, which is important to me. Trying to load one will make it look like this.
I've found a few other topics about it (this and this) but they're all about WPF and use BitmapImage. I've searched about how to use BitmapImage in a Winforms application, but haven't found anything apart from that it is supposed to somehow work.
I would like to stay with Winforms because I'm just getting used to it, but that's not a necessity.
To summarize: I need a way to show common image types (png, jpg, bmp, and animated gif) while still being able modify the file on the HDD. It is OK if that means unloading->modifying->reloading the file. I'd prefer Winforms, but other Frameworks would do.
Thanks for your help.
Edit: Another way I've tried
using (System.IO.FileStream fs = new System.IO.FileStream("E:\\Pics\\small.gif", System.IO.FileMode.Open, System.IO.FileAccess.Read))
{
System.IO.MemoryStream ms = new System.IO.MemoryStream();
fs.CopyTo(ms);
pic.Image = Image.FromStream(ms);
}
But shows the same problem as the second example. The gif doesn't load.
Using a MemoryStream is indeed the right way to avoid the file lock. Which is a strong optimization btw, the lock is created by the memory-mapped file that the Image class uses to keep the pixel data out of the paging file. That matters a great deal when the bitmap is large. Hopefully not on an animated gif :)
A small mistake in your code snippet, you forgot to reset the stream back to the start of the data. Fix:
using (var fs = new System.IO.FileStream(...)) {
var ms = new System.IO.MemoryStream();
fs.CopyTo(ms);
ms.Position = 0; // <=== here
if (pic.Image != null) pic.Image.Dispose();
pic.Image = Image.FromStream(ms);
}
In case it needs to be said: do not dispose the memory stream. That causes very hard to diagnose random crashes later, pixel data is read lazily.
Essentialy you'll have to make a copy of the image file in your memory.
Pre .Net 4.0 (2.0,3.0,3.5) you'd have to create a FileStream and copy it to a MemoryStream and rewind it, as seen in another answer.
Since .Net 4.0 (4.0,4.5,...) Image.FromFile supports animated GIF's
If you work with .Net 4.0 or later following method will suffice:
Using System.IO, System.Drawing and System.Drawing.Imaging
private void Form1_Load(object sender, EventArgs e)
{
string szTarget = "C:\\someImage.gif";
PictureBox pic = new PictureBox();
pic.Dock = DockStyle.Fill;
Image img = Image.FromFile(szTarget); // Load image fromFile into Image object
MemoryStream mstr = new MemoryStream(); // Create a new MemoryStream
img.Save(mstr, ImageFormat.Gif); // Save Image to MemoryStream from Image object
pic.Image = Image.FromStream(mstr); // Load Image from MemoryStream into PictureBox
this.Controls.Add(pic);
img.Dispose(); // Dispose original Image object (fromFile)
// after this you should be able to delete/manipulate the file
File.Delete(szTarget);
}

Loading Images in BackgroundWorker

I have been dealing with a problem with a backgroundWorker these last couple of days. I have been looking through forums and documentation on MSDN but still haven't found the answer so now I want to ask you clever people.
Long story short, I have a custom user control consisting of a WrapPanel inside a ScrollViewer. The WrapPanel contains some elements that are notified when they are scrolled into view.
The elements are then supposed to load and display an image, and this is where the problem comes in. In order to not lock the gui thread i load the images in a BackgroundWorker, but the GUI stalls anyways. This is the code for the class that represents the elements contained in the WrapPanel:
class PictureThumbnail : INotifyingWrapPanelElement
{
private string path;
private Grid grid = null;
private BackgroundWorker thumbnailBackgroundCreator = new BackgroundWorker();
private delegate void GUIDelegate();
private Image thumbnailImage = null;
public PictureThumbnail(String path)
{
this.path = path;
visible = false;
thumbnailBackgroundCreator.DoWork += new DoWorkEventHandler(thumbnailBackgroundCreator_DoWork);
}
void thumbnailBackgroundCreator_DoWork(object sender, DoWorkEventArgs e)
{
BitmapImage bi = LoadThumbnail();
bi.Freeze(); //If i dont freeze bi then i wont be able to access
GUIDelegate UpdateProgressBar = delegate
{
//If this line is commented out the GUI does not stall. So it is not the actual loading of the BitmapImage that makes the GUI stall.
thumbnailImage.Source = bi;
};
grid.Dispatcher.BeginInvoke(UpdateProgressBar);
}
public void OnVisibilityGained(Dispatcher dispatcher)
{
visible = true;
thumbnailImage = new Image();
thumbnailImage.Width = 75;
thumbnailImage.Height = 75;
//I tried setting the thumbnailImage.Source to some static BitmapImage here, and that does not make the GUI stall. So it is only when it is done through the GUIDelegate for some reason.
grid.Children.Add(thumbnailImage);
thumbnailBackgroundCreator.RunWorkerAsync();
}
private BitmapImage LoadThumbnail()
{
BitmapImage bitmapImage = new BitmapImage();
// BitmapImage.UriSource must be in a BeginInit/EndInit block
bitmapImage.BeginInit();
bitmapImage.UriSource = new Uri(path);
bitmapImage.DecodePixelWidth = 75;
bitmapImage.DecodePixelHeight = 75;
bitmapImage.EndInit();
return bitmapImage;
}
}
I have added some comments in the code explaining some stuff I tried and what leads I have. But I'll write it again here. If I just load the BitmapImage in the backgroundWorker, but don't apply it as the Source of the thumbnailImage the GUI doesn't stall (but no image is displayed obviously). Also if I set the Source of the thumbnailImage to some preloaded static BitmapImage in the OnVisibilityGained method (So in the GUI thread), then the GUI wont stall, so it is not the actual setting of the Image.Source that's the culprit.
You should make use of the reporting feature of the backgroundworker which lets you directly access the controls of your form without invoking.

Silverlight 4 BitmapImage bug : ImageOpened not invoked after SetSource()

This seems like a serious bug :
private void LayoutRoot_Drop(object sender, DragEventArgs e)
{
if ((e.Data != null) && (e.Data.GetDataPresent(DataFormats.FileDrop)))
{
FileInfo[] files = (FileInfo[])e.Data.GetData(DataFormats.FileDrop);
using (FileStream fileStream = files[0].OpenRead())
{
//Code reaching this point.
BitmapImage bmpImg = new BitmapImage();
bmpImg.ImageOpened += new EventHandler<RoutedEventArgs>(bmpImg_ImageOpened);
bmpImg.ImageFailed += new EventHandler<ExceptionRoutedEventArgs>(bmpImg_ImageFailed);
try
{
bmpImg.SetSource(fileStream);
}
catch
{
//Code dosen't reach here.
}
}
}
}
void bmpImg_ImageFailed(object sender, ExceptionRoutedEventArgs e)
{
//Code dosen't reach here.
}
void bmpImg_ImageOpened(object sender, RoutedEventArgs e)
{
//Code dosen't reach here.
}
I am experiencing a very strange behivour. Running this code on my computer, it works - when you drag a JPG on the LayoutRoot I can break inside bmpImg_ImageOpened().
But on a different machine it won't work - when dragging a JPG, I can break in the drop event but after SetSource() nothing happens : no exceptions are thrown, and non of the callbacks are invoked.
I tried it on another machine and it also didn't work.
edit:
On all of the machines, when adding an Image class and setting it's Source property to the bitmapImage, the image is shown fine. so I guess it's an issue with the callbacks. This is not enough because I still need those events.
I am banging my head here, what could it be ?
This is simply how Silverlight has always behaved. ImageOpened only fires if the image is downloaded and decoded (i.e. using Source). It does not fire when using SetSource. If you need access to the dimensions after loading your image either use WriteableBitmap for the PixelWidth and PixelHeight properties (instead of BitmapImage) or do something like:
img.Source = bmpImg;
Dispatcher.BeginInvoke(() =>
{
FakeImageOpened(); // Do logic in here
});
You have to set
bitmapImage.CreateOptions = BitmapCreateOptions.None;
Then the ImageOpened event is fired. This is because the default Options are CreateDelayed
Greetings
Christian
http://www.wpftutorial.net

Categories

Resources