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.
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 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.
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 am building my first app in windows phone 7 application. I have an image which comes from the web and when the image is clicked i am navigated to another page.
My xaml code is:
<Button Click="Image_Click" Name="image1" Margin="-33,-16,-26,-13">
<Button.Background>
<ImageBrush Stretch="Fill" ImageSource = "http://political-leader.vzons.com/ArvindKejriwal/images/icons/landing.png"/>
</Button.Background>
</Button>
My .cs code is
private void Image_Click(object sender, RoutedEventArgs e)
{
NavigationService.Navigate(new Uri("/AAP.xaml", UriKind.Relative));
}
Now the issue is that i want to store the image so that it can be even viewed while offline. Can anyone help me what modification should i do to for this purpose.
This is a working example on how to do that. The logic is as follow :
In the page's constructor, call LoadImage method.
The method will set imageBrush's ImageSource to image in Isolated storage if available.
If the image not exists in Isolated storage, LoadImage method will download the image from web and call an event handler when download completed.
The event handler (DownloadCompleted method) will then, save the image to Isolated Storage and call LoadImage again. Refer to point 2 for what happen next.
You may want to improve it later to implement MVVM and using DataBinding.
References: nickharris.net, geekchamp.com
string imageName = "myImage.jpg";
string imageUrl = "http://political-leader.vzons.com/ArvindKejriwal/images/icons/landing.png";
public MainPage()
{
InitializeComponent();
LoadImage();
}
private void LoadImage()
{
BitmapImage bi = new BitmapImage();
using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
//load image from Isolated Storage if it already exist
if (myIsolatedStorage.FileExists(imageName))
{
using (IsolatedStorageFileStream fileStream = myIsolatedStorage.OpenFile(imageName, FileMode.Open, FileAccess.Read))
{
bi.SetSource(fileStream);
imageBrushName.ImageSource = bi;
}
}
//else download image to Isolated Storage
else
{
WebClient wc = new WebClient();
wc.OpenReadCompleted += new OpenReadCompletedEventHandler(DownloadCompleted);
wc.OpenReadAsync(new Uri(imageUrl, UriKind.Absolute), wc);
}
}
}
private void DownloadCompleted(object sender, OpenReadCompletedEventArgs e)
{
if (e.Error == null && !e.Cancelled)
{
try
{
using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
IsolatedStorageFileStream fileStream = myIsolatedStorage.CreateFile(imageName);
BitmapImage bitmap = new BitmapImage();
bitmap.SetSource(e.Result);
WriteableBitmap wb = new WriteableBitmap(bitmap);
// Encode WriteableBitmap object to a JPEG stream.
Extensions.SaveJpeg(wb, fileStream, wb.PixelWidth, wb.PixelHeight, 0, 85);
fileStream.Close();
}
//after image saved to Iso storage, call LoadImage method again
//so the method will set imageBrush's ImageSource to image in Iso storage
LoadImage();
}
catch (Exception ex)
{
//Exception handle appropriately for your app
}
}
else
{
//Either cancelled or error handle appropriately for your app
}
}
When my page loads and I press button1 I can get the image and can see it.
But when I click it second time it doesn't work at all. I debugged it button1_Click(...) and I am sure that imageData != null.
I really can't figure out what's up... Please help me!
private void button1_Click(object button, RoutedEventArgs e)
{
Guid sid = Guid.Parse("087462df-e4b6-484c-879e-cccc37b4c1f4");
EntityQuery<Screenshot> screen = this._myDomainContext.GetScreenshotQuery(sid);
this._myDomainContext.Load(screen).Completed += (sender, args) =>
{
try
{
byte[] imageData = (((LoadOperation<Screenshot>)sender).Entities.FirstOrDefault()).Screen;
if (imageData != null)
{
BitmapImage img = Utilities.Graphics.GetImage(imageData);
img.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
image1.Source = null;
image1.Source = img;
}
}
catch
{
}
};
}
and
public static BitmapImage GetImage(byte[] rawImageBytes)
{
BitmapImage imageSource = null;
try
{
using (MemoryStream stream = new MemoryStream(rawImageBytes))
{
stream.Seek(0, SeekOrigin.Begin);
BitmapImage b = new BitmapImage();
b.SetSource(stream);
imageSource = b;
}
}
catch
{
}
return imageSource;
}
Try changing the overload of Load:
this._myDomainContext.Load(screen, LoadBehavior.RefreshCurrent, true).Completed+= ...
I'm not completely sure on the cause if your issue, but I have a few pointers on the code with regards to using BitmapImage's.
There is no need to set the image1.Source property to null before assigning it with an actual source.
Don't dispose the stream you pass to the BitmapImage in case the control is trying to read from it.
I personally make use of BeginInit(...), StreamSource and EndInit(...) when working with BitmapImage's.