I am working on a project that requires me to get bunch of images and display them as a video. Which means, I will update Image control of WPF 30 times a second.
So far no luck.
BitmapImage img = BitmapToImageSource((Bitmap)image);
this.image_box.Dispatcher.Invoke(() =>
{
//this.image_box.Source.Freeze();
this.image_box.Source = img;
});
BitmapImage BitmapToImageSource(Bitmap bitmap)
{
using (MemoryStream memory = new MemoryStream())
{
bitmap.Save(memory, System.Drawing.Imaging.ImageFormat.Bmp);
memory.Position = 0;
BitmapImage bitmapimage = new BitmapImage();
bitmapimage.BeginInit();
bitmapimage.StreamSource = memory;
bitmapimage.CacheOption = BitmapCacheOption.OnLoad;
bitmapimage.EndInit();
return bitmapimage;
}
}
image_box is the image control (wpf).
So, I was expecting this code to update image and give an illusion of video being played. Whereas it does nothing. I see waiting cursor on the window.
Edited ---- More Info ----
I created a simple program to check if images can be changed in image control. But the results are same. I see waiting cursor and no image.
private void Window_Loaded(object sender, RoutedEventArgs e)
{
ImageSource s1;
ImageSource s2;
s1 = new BitmapImage(new Uri(#"IMAGE_PATH", UriKind.Absolute));
s2 = new BitmapImage(new Uri(#"IMAGE_PATH", UriKind.Absolute));
while (true)
{
try
{
if (image.Source == s1)
image.Source = s2;
else
image.Source = s1;
System.Threading.Thread.Sleep(1000);
}
catch(Exception ex)
{
MessageBox.Show("Error");
}
}
}
Although I knew this fact, but still made the same mistake.
We are not allowed to update GUI in a loop. If we want to do so, we need to create a separate thread.
Related
I have a custom UserControl with an Image control in it. I'm trying to fetch an image from web[network server] and show it in my control, refreshing the source using a dispatcher timer. Here is the code:
void StartSourceRefresh()
{
if (timeinterval < 1) timeinterval = 1;
tmrRefresh.Tick += new EventHandler(dispatcherTimer_Tick);
tmrRefresh.Interval = new TimeSpan(0, 0, timeinterval); //in hour-minute-second
tmrRefresh.Start();
}
public void ChangeImageSource(string newSource)
{
//newSource = "http://192.168.1.3/abc/imagetobeshown.png"
WebImg.Source = null;
if (newSource.Trim() == "")
WebImg.Source = new BitmapImage(new Uri(#imagePlaceholder, UriKind.Absolute));
else
{
BitmapImage image = new BitmapImage();
image.BeginInit();
image.UriSource = new Uri(#newSource, UriKind.Absolute);
image.EndInit();
WebImg.Source = image;
}
}
private void dispatcherTimer_Tick(object sender, EventArgs e)
{
ChangeImageSource(txtImgSrc.Text.Trim());
}
the problem is the image wont change. Its showing the same one which was fetched at the first time. Timer is running fine. But the image just won't change. What am i doing wrong here?
Edit: Network Source gets refreshed after certain interval, so have to fetch the same source
You are apparently reloading from the same image URL, which is cached by default.
Disable caching by setting BitmapCreateOptions.IgnoreImageCache:
var image = new BitmapImage();
image.BeginInit();
image.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
image.UriSource = new Uri(newSource);
image.EndInit();
I'm c# begginer.I used wpf image control to show image from my camera. It's work normally in winform pictureBox but not in WPF image control. It's all blank! And I'm sure the data stream exist.
The icImagingControl1 is the control of my camera SDK.
It's can return image data in many forms. Like Intptr,Byte,Bitmap.
I want load data in image control from memory.
And the function would be triggered everytime the camera snap.
Here is my code.
private void icImagingControl1_ImageAvailable(object sender, XXX.Imaging.ICImagingControl.ImageAvailableEventArgs e)
{
XXX.Imaging.ImageBuffer CurrentBuffer = null;
CurrentBuffer = icImagingControl1.ImageBuffers[e.bufferIndex];
try
{
using (MemoryStream memory = new MemoryStream())
{
CurrentBuffer.Bitmap.Save(memory, ImageFormat.Bmp);
memory.Position = 0;
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = memory;
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.EndInit();
image1.Source = bitmapImage;
}
}
catch (Exception ex)
{
System.Diagnostics.Trace.WriteLine(ex.Message);
}
}
Hope anyone could help me.
My Question: Is there a way to load images into a BitmapImage that will not take up a tremendous amount of memory and the images can still be deleted? Read below for more details:
I have a class PhotoCollection : ObservableCollection<Photo>{ }, where the Photo class creates a BitmapImage Object:
PhotoCollection Class:
public class PhotoCollection : ObservableCollection<Photo>
{
...Stuff in here...
}
Photo Class:
public class Photo
{
public Photo(string path)
{
_path = path;
_source = new Uri(path);
BitmapImage tmp = new BitmapImage();
tmp.BeginInit();
tmp.UriSource = _source;
tmp.CacheOption = BitmapCacheOption.None;
tmp.DecodePixelWidth = 200;
tmp.DecodePixelHeight = 200;
tmp.EndInit();
BitmapImage tmp2 = new BitmapImage();
tmp2.BeginInit();
tmp2.UriSource = _source;
tmp2.CacheOption = BitmapCacheOption.None;
tmp2.EndInit();
_image = BitmapFrame.Create(tmp2, tmp);
_metadata = new ExifMetadata(_source);
}
public BitmapFrame _image;
public BitmapFrame Image { get { return _image; } set { _image = value; } }
...More Property Definitions used to support the class
}
When I drag and drop images on my computer into a listbox the photos are loaded into the PhotoCollection of Photos and displayed within a listbox(Thanks to Binding). If I drop 50MBs of photos my program takes up ~50MBs of memory.
The problem I have is I need to later delete these photos from the folder. To do this I must unload or dispose of the photos in memory first, because BitmapImage locks the files. I cannot figure out how to do this.
I thought after finding this similar StackOverFlow Question that all my problems were solved. Implementing the code from the StackOverFlow's Question:
public class Photo
{
public Photo(string path)
{
BitmapImage tmp = new BitmapImage();
BitmapImage tmp2 = new BitmapImage();
tmp = LoadImage(_path);
tmp2 = LoadImage(_path);
...
}
private BitmapImage LoadImage(string myImageFile)
{
BitmapImage myRetVal = null;
if (myImageFile != null)
{
BitmapImage image = new BitmapImage();
using (FileStream stream = File.OpenRead(myImageFile))
{
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.StreamSource = stream;
image.EndInit();
}
myRetVal = image;
}
return myRetVal;
}
...
}
There was just one HUGE issue with implementing a FileStream to load the images into the BitMapImage object. My memory use skyrocketed! Like 50MBs of photos took up 1GB of memory and took 10 times longer to load:
Link to Image
To reiterate my Question: Is there a way to load images into a BitmapImage that will not take up a tremendous amount of memory and the images can still be deleted?
Thanks so much! ^_^
You can set the DecodePixelWidth and DecodePixelHeight properties of the BitmapImage to tell it to load fewer pixels into memory.
Ik in windows forms I could add images in resources and then change the images as users click on an event handler not sure whats changed in Xaml but I cant figure it out.
private void guessClick(object sender, RoutedEventArgs e)
{
wrongGuesses++;
hangmanPicture.Image = hangmanImage[wrongGuesses];
}
if I just put hangmanPicture = hangmanImage[wrongGuesses];
I get can not convert. I don't understand why its trying to convert anything.
if your hangmanImage array is an array of ImageSource or BitmapImage, you can use it like this:
private void guessClick(object sender, RoutedEventArgs e)
{
wrongGuesses++;
hangmanPicture.Source = hangmanImage[wrongGuesses];
}
Otherwise, you have to convert anything in hangmanImage into ImageSource or BitmapImage.
If it's Bitmap you can use below converter before that code:
public static BitmapImage ConvertToBitmapImageFromBitmap(Bitmap bitmap)
{
using(var memory = new MemoryStream())
{
BitmapImage bitmapImage;
bitmap.Save(memory, ImageFormat.Png);
memory.Position = 0;
bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = memory;
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.EndInit();
return bitmapImage;
}
}
So, then it will be like this:
hangmanPicture.Source = ConvertToBitmapImageFromBitmap(hangmanImage[wrongGuesses]);
I've been using the BitmapImage.DownloadProgress event to wait until the image is loaded when creating a bitmap from a URI. That was I can use an event handler to check the Image.ActualWidth/ActualHeight after the download is complete. This works fine.
But when I am loading an image that the user has selected from their computer, the DownloadProgress is useless.
This works for URI bitmaps:
private void LoadImage(Uri uri)
{
this.ResetProgressImage();
BitmapImage image = new BitmapImage();
image.DownloadProgress += new EventHandler<DownloadProgressEventArgs>(LoadImageDownloadProgress);
image.UriSource = uri;
this.ImageFull.Source = image;
}
private void LoadImageDownloadProgress(object sender, DownloadProgressEventArgs e)
{
this.Dispatcher.BeginInvoke(delegate
{
this.ProgressImage.Value = e.Progress;
if (e.Progress == 100)
{
ImageHelper.Current.Width = Math.Round(this.ImageFull.ActualWidth);
ImageHelper.Current.Height = Math.Round(this.ImageFull.ActualHeight);
}
});
}
But this doesn't work when getting a BitmapImage from a stream:
private void LoadImage(Stream stream)
{
this.ResetProgressImage();
BitmapImage image = new BitmapImage();
image.DownloadProgress += new EventHandler<DownloadProgressEventArgs>(LoadImageDownloadProgress);
image.SetSource(stream);
this.ImageFull.Source = image;
}
Does anyone know an alternative to DownloadProgress, when using SetSource(stream)?
There is no event in BitmapImage to monitor the loading progress using a stream.
However, if you just need the image dimensions, you can add the image to the hierarchy and wait for the "Loaded" or "LayoutUpdated" event.