If I've got a Silverlight Image control that downloads from a full URL, how can I get the size (in bytes) of the downloaded image without making another web call?
I can't find anything on the Image or the BitmapImage source behind it that would tell me. And even the DownloadProgress event on the BitmapImage only gives a percentage.
I never noticed it before, but that is kind of a strange gap in the framework...
You'll probably have to download the image by itself using a WebClient object. That'll give you a stream of bytes. You can check the length of the stream, and then create a bitmap from the stream.
Code to set up the web client and begin the download (note, it's an async call, so we assign an event handler to fire when it completes the download.)
WebClient wc = new WebClient();
wc.OpenReadCompleted += new OpenReadCompletedEventHandler(wc_OpenReadCompleted);
Uri someImageUri = new Uri("http://www.somesite.com/someimage.jpg");
wc.OpenReadAsync(someImageUri);
Here's an example of what the event handler method might look like:
void wc_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
System.IO.Stream imageStream = e.Result;
long imageSize = imageStream.Length;
BitmapImage bi = new BitmapImage();
bi.SetSource(imageStream);
Image image = new Image();
image.Source = bi;
}
Obviously, if you already have an image control on your form, you wouldn't need to create a new one, or if you did want to create it, you'll have to add it to a parent panel of some kind...
~Chris
Related
I'm creating BitmapImages, to download images from IP cam.
I use DownloadCompleted, DownloadFailed and DownloadProgress events.
But every hour, I reset everything. Those images are created dynamically, and I'd like to clear them (or destroy them) when I reset. But I don't find them, and they continue to fire the events....
Here the example :
void dispatcherTimer_Tick(object sender, EventArgs e)
{
BitmapImage img = New BitmapImage();
img.BeginInit();
img.CacheOption = BitmapCacheOption.None;
img.UriCachePolicy = new RequestCachePolicy(RequestCacheLevel.NoCacheNoStore);
img.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
img.DownloadCompleted += DownloadCompleted;
img.DownloadFailed += DownloadFailed;
img.DownloadProgress += bi_DownloadProgress;
img.UriSource = new Uri(xR["URLJPG"].ToString());
//xR["URLJPG"] is a DataViewRow, that contains the url to download for each image)
img.EndInit();
}
There is several images created this way. I'd like to clear ou destroy them all.
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.
I am working on a C# project, and I am having a strange problem. I have an Image control and I am trying to display an image within it. There is no error or exception thrown, but the image doesn't display.
Below is the code I am using:
BitmapImage image = new BitmapImage();
image.UriSource = new Uri("images\\low_battery.png", UriKind.Relative);
imageIcon.Source = image;
imageIcon.Visibility = System.Windows.Visibility.Visible;
I have the directory image and the image file low_battery.png running in the same directory as to where the executable is running from.
Thanks for any help you can provide.
A few suggestions:
1) You can get the exception from WPF if you subscribe to the ImageFailed even on imageIcon and the and DownloadFailed event on BitmapImage. Then put breakpoints in those methods and see what it says.
2) Before setting UriSource, call image.BeginInit(). After setting UriSource, call image.EndInit(). To help debug the problem, you can subscribe to the ImageFailed event on imageIcon and DownloadFailed on BitmapImage. So the final code looks like this:
BitmapImage image = new BitmapImage();
image.BeginInit();
image.UriSource = new Uri("/images/low_battery.png", UriKind.Relative);
image.DownloadFailed += image_DownloadFailed;
image.EndInit();
imageIcon.ImageFailed += imageIcon_ImageFailed;
imageIcon.Source = image;
imageIcon.Visibility = System.Windows.Visibility.Visible;
and further down:
void imageIcon_ImageFailed(object sender, ExceptionRoutedEventArgs e)
{
// Breakpoint here
}
void image_DownloadFailed(object sender, ExceptionEventArgs e)
{
// Breakpoint here
}
3) Another way to do this is to embed images directly into your project. Do that by adding an images folder into your project, then adding the image, and setting the type to "Resource"
Either call image.BeginInit() before setting image.UriSource and image.EndInit() afterwards, or simply use the BitmapImage constructor that takes an Uri as argument:
var uri = new Uri("images\\low_battery.png", UriKind.Relative);
imageIcon.Source = new BitmapImage(uri);
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);
}
I'm having some trouble using ZXing in silverlight.
I'm using this ZXing port: http://zxingnet.codeplex.com/
My proejct is able to get the video feed from the webcam, but I'm stuck at this line.
This is how i get the feed:
CaptureSource _capture = new CaptureSource();
_capture.VideoCaptureDevice = CaptureDeviceConfiguration.GetDefaultVideoCaptureDevice();
videoBrush = new VideoBrush();
videoBrush.Stretch = Stretch.Uniform;
videoBrush.SetSource(_capture);
webcam.Fill = videoBrush;
if (CaptureDeviceConfiguration.AllowedDeviceAccess||CaptureDeviceConfiguration.RequestDeviceAccess())
{
try
{
_capture.Start();
}
catch (Exception E)
{
MessageBox.Show(E.Message);
}
}
LuminanceSource source = new RGBLuminanceSource(,webcam.Width, webcam.Height);
It says, that says that it need a byte array, the "rbgRawBytes".
I got a videobrush which contains the webcam stream, i think :)
and i got the webcam rectangle that displays the output.
You should use the method CaptureImageAsync and the event CaptureImageCompleted. In the event handler you get a WriteableBitmap within the event arguments. The WriteableBitmap is a captured image from the webcam. Use the WriteableBitmap instance directly with the method Decode of the BarcodeReader class. Don't do it manually by using RGBLuminanceSource.
Here is a good sample how you can use CaptureImageAsync and CaptureImageCompleted:
http://channel9.msdn.com/coding4fun/articles/FaceLight--Silverlight-4-Real-Time-Face-Detection