NullReference Exception Occurs in Image Source wpf - c#

I am using FileSystemWatcher to get the latest image from my Assets folder, i have a webcam to capture the image and save it in the Assets folder. After saving the image i get the latest image from FileSystemWatcher event.
Here is my code :
//FileWatcher
private void FileWatcher()
{
path = #"..\..\Assets\WebCamImage\";
System.IO.FileSystemWatcher watcher = new FileSystemWatcher(path);
watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName;
watcher.Changed += watcher_Changed;
watcher.EnableRaisingEvents = true;
}
//Event
void watcher_Changed(object sender, FileSystemEventArgs e)
{
CustomerImage.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal,
new Action(
delegate()
{
CustomerImage.Source = (ImageSource)isc.ConvertFromString(e.FullPath);
}
));
}
At page load event the source of CustomerImage control is set to a default picture which is nopictureavail.jpeg, when a changes made in that particular Directory the image should populate in CustomerImage, filewatcher event fires then the error throws at
CustomerImage.Source = (ImageSource)isc.ConvertFromString(e.FullPath);
NullReferenceException Occured in presentationCore.dll

Try to add this:
bitmap.CreateOption = BitmapCreateOptions.IgnoreImageCache
Here's some more
https://stackoverflow.com/a/1689808/2609288

You're getting this error because WPF holds on to the handle from its images. Try using this code instead:
BitmapImage image = new BitmapImage();
try
{
using (FileStream stream = File.OpenRead(filePath))
{
image.BeginInit();
image.StreamSource = stream;
image.CacheOption = BitmapCacheOption.OnLoad;
image.EndInit();
}
}
catch { return DependencyProperty.UnsetValue; }
I have this code in an IValueConverter to avoid a similar problem.

Related

Why my image update is not "binded"?

I have a gird view of my objects "Sessions". On user selection, every Session's Image gets display in an Image on my GUI. I have this routine where, on user SelectedSession, it Deletes any existing image, Takes a new Image frameSource, Saves it locally and then sets the private member variable ImagePath as its path:
public Session SelectedSession { get { return selectedSession; } set { SetValue(ref selectedSession, value); } }
public BitmapSource SessionImage { get { return sessionImage; } private set { SetValue(ref sessionImage, value); } }
//..
public void TakeSessionImage(BitmapSource frameSource)
{
if (SelectedSession != null)
{
if (File.Exists(SelectedSession.ImagePath))
File.Delete(SelectedSession.ImagePath); // delete old - works
SelectedSession.ImagePath = FileStructure.CurrentSessionPath + "\\" + SelectedSession.Name + ".png"; // set ImagePath - works - Technically it does not change. I kept it if I needed later to add anything to the file name like GUID or whatever
ImageIO.RotateAndSaveImage(SelectedSession.ImagePath, (WriteableBitmap)frameSource, -270); // save new image - works
SessionImage = SelectedSession.LoadImageFromFile(); // binded the image to display
}
}
Binding in Xaml:
<Image x:Name="currentSessionImage" Source="{Binding SessionImage}"/>
In "Session.cs" class:
public BitmapImage LoadImageFromFile()
{
if (File.Exists(ImagePath)) // image path is correct
{
try
{
BitmapImage image = new BitmapImage();
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.UriSource = new Uri(ImagePath);
image.EndInit();
return image;
}
catch
{
return null;
}
}
return null;
}
This has been debugged and all statements are valid. SessionImageis my property that is "binded" to an Image on my GUI. However, a very weird behavior is happening:
It is deleting the old image file and I can see that in windows explorer that the file is gone.
It is saving the new one correctly
It is loading the new image from the correct path
But then, it only displays the very First Image I ever took.
Whatever new image I send. It always displays the same image I took the first time. Could anyone please check it for me? I validated the whole code and all values are logical. No syntax errors anywhere.
Edit:
protected virtual bool SetValue<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (!EqualityComparer<T>.Default.Equals(storage, value))
{
storage = value;
OnPropertyChanged(propertyName);
return true;
}
return false;
}
//..
protected void OnPropertyChanged(string propertyName)
{
try
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
catch (Exception ex)
{
}
}
WPF caches bitmaps that are loaded from URIs. To avoid that, load the BitmapImage directly from file instead:
using (var fileStream = new FileStream(ImagePath, FileMode.Open, FileAccess.Read))
{
BitmapImage image = new BitmapImage();
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.StreamSource = fileStream;
image.EndInit();
return image;
}

How to download two images asynchronously and only after doing something

I am using VS2010 on a WPF app. I cannot use the async feature.
Basically I need to load two images, and after images are downloaded a need to print them.
I need to manage the downloading of both images. How to achieve this?
var bitmap = new BitmapImage(new Uri("http://abcd.com/logo.png"));
var bitmapB = new BitmapImage(new Uri("http://abcd.com/logoB.png"));
if (!bitmap.IsDownloading)
{
// print immediately code here
}
else
{
bitmap.DownloadCompleted += (o, e) =>
{
// print when download completed
};
}
if (!bitmapB.IsDownloading)
{
// print immediately
}
else
{
bitmapB.DownloadCompleted += (o, e) =>
{
// print when download completed
};
}
It sounds like what you really want is the WebClient Class. You can use a number of its methods to download either the image as a file, or as a byte collection. The best part is that you can also do this asynchronously, so it will all happen on a background thread automatically.
When using these asynchronous methods, you need to handle the relevant Download...Completed event to retrieve the results. From the DownloadStringCompletedEventHandler Event page on MSDN:
public static void DownLoadFileInBackground2 (string address)
{
WebClient client = new WebClient ();
Uri uri = new Uri(address);
// Specify that the DownloadFileCallback method gets called
// when the download completes.
client.DownloadFileCompleted += new AsyncCompletedEventHandler(
DownloadFileCallback2);
// Specify a progress notification handler.
client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(
DownloadProgressCallback);
client.DownloadFileAsync (uri, "serverdata.txt");
}
Take a look at the WebClient.DownloadDataAsync Method and WebClient.DownloadFileAsync Method pages on the MSDN website for more information.
Something like the following code should do the job for a variable number of images. It downloads all images sequentially in a ThreadPool thread before invoking a PrintImages method in the UI thread. Note the after downloading each image is frozen (by image.Freeze()) in order to make it cross-thread accessible.
private void DownloadAndPrintImagesAsync(IEnumerable<string> urls)
{
ThreadPool.QueueUserWorkItem(o =>
{
var images = urls.Select(url => DownloadImage(url));
Dispatcher.Invoke(new Action(() => PrintImages(images)));
});
}
private BitmapImage DownloadImage(string url)
{
var buffer = new WebClient().DownloadData(url);
var image = new BitmapImage();
using (var stream = new MemoryStream(buffer))
{
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.StreamSource = stream;
image.EndInit();
}
image.Freeze();
return image;
}
private void PrintImages(IEnumerable<BitmapImage> images)
{
// print here
}
You might improve this by issuing multiple downloads in parallel, but that would complicate the code. You would have to wait for all asynchronous downloads to be finished before printing.
Update: Based on the proposal given by Sheridan, you may modify the DownloadAndPrintImagesAsync method like this:
private List<BitmapImage> images = new List<BitmapImage>();
private void DownloadAndPrintImagesAsync(IEnumerable<string> urls)
{
foreach (var url in urls)
{
var webClient = new WebClient();
webClient.DownloadDataCompleted += ImageDownloadCompleted;
webClient.DownloadDataAsync(new Uri(url));
}
}
private void ImageDownloadCompleted(object sender, DownloadDataCompletedEventArgs e)
{
if (!e.Cancelled && e.Error == null)
{
var image = new BitmapImage();
using (var stream = new MemoryStream(e.Result))
{
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.StreamSource = stream;
image.EndInit();
}
images.Add(image);
if (images.Count == 2) // or whatever
{
// print images
}
}
}

display an image from file in wpf isn't working?

I have a button b3 and an image named pictureBox1 . Im using WPF, however I'm using the winforms openFileDialog instead of the one that comes with WPF :
below is the code that I put inside the click event of my button :
private void b3_Click(object sender, RoutedEventArgs e)
{
System.Windows.Forms.OpenFileDialog openDialogIcon = new System.Windows.Forms.OpenFileDialog();
if (openDialogIcon.ShowDialog() == System.Windows.Forms.DialogResult.OK) {
Image i = new Image();
BitmapImage src = new BitmapImage();
src.BeginInit();
src.UriSource = new Uri(openDialogIcon.FileName, UriKind.Absolute);
src.CacheOption = BitmapCacheOption.OnLoad;
src.EndInit();
i.Source = src;
i.Stretch = Stretch.Uniform;
//int q = src.PixelHeight; // Image loads here
}
}
When I click the button and select an icon. The icon doesn't appear in the pictureBox1.
Can someone please explain why the code above doesn't show the icon inside the pictureBox?
You need to assign your image to the pictureBox, else you wont see it on your screen and you only made the image object in memory.
private void b3_Click(object sender, RoutedEventArgs e)
{
System.Windows.Forms.OpenFileDialog openDialogIcon = new System.Windows.Forms.OpenFileDialog();
if (openDialogIcon.ShowDialog() == System.Windows.Forms.DialogResult.OK) {
BitmapImage src = new BitmapImage();
src.BeginInit();
src.UriSource = new Uri(openDialogIcon.FileName, UriKind.Absolute);
src.CacheOption = BitmapCacheOption.OnLoad;
src.EndInit();
pictureBox1.Source = src;
}
}
Try to drag and drop a Image control in your window
...
//imageStretch <- the name of Image control
i.Stretch = Stretch.Uniform;
//int q = src.PixelHeight; // Image loads here
imageStretch.Source = src;
...

ImageTools and Webclient

I am trying to use webclient check download a stream before it is handled by an ExtendedImage, because my app is showing a bug when the uri is not found.
So my solution is to load the image first and then read the webclient result into the extended image.
This is what I am trying to do.
WebClient wc = new WebClient();
wc.OpenReadAsync(Uri);
wc.OpenReadCompleted += delegate(object Sender, OpenReadCompletedEventArgs e){
Logo = new BitmapImage();
ExtendedImage hExtendedImage = new ExtendedImage();
try
{
hExtendedImage.SetSource(e.Result);
Logo.SetSource(hExtendedImage.ToStream());
}
catch (WebException)
{
}
};
but now I am getting an "image is not loaded" error from hExtendedImage on this line
Logo.SetSource(hExtendedImage.ToStream());
I'm obviously loading the image from e.Result into the hExtendedImage wrong.
var client = new WebClient();
// Always define event handlers,
// BEFORE calling any method that could invoke them.
client.OpenReadCompleted += (s1, e1)
{
Logo = new BitmapImage();
var extendedImage = new ExtendedImage();
extendedImage.OnLoadingCompleted += (s2, e2)
{
// Invoke the dispatcher, so we're sure it's set on the UI thread.
Dispatcher.BeginInvoke(new Action
(
() => Logo.SetSource(extendedImage.ToStream()))
);
};
extendedImage.SetSource(e1.Result);
};
client.OpenReadAsync(Uri);
Unfortunately SetSource is ansyc. Use the event LoadingCompleted of hExtendedImage to set Logo source.
Be careful: LoadingCompleted callback is not in ui thread! You must invoke dispatcher if you want to change UI controls like Image.
From ExtendedBitmap source on CodePlex:
public void SetSource(Stream stream)
{
Contract.Requires<ArgumentNullException>(stream != null, "Stream cannot be null.");
if (_uriSource == null)
{
LoadAsync(stream);
}
}

How to Update/replace image in WPF by clicking button

So I have an image that when the user clicks on a button it will change it to a new item. However, whenever the user clicks on one of the button, the window will go blank. How can I get this to work? Thank you.
private void Next_Click(object sender, RoutedEventArgs e)
{
if (imageNumber > 6)
{
imageNumber = 1;
}
imageNumber++;
string sUri = string.Format("#/Resources/{0}", imageSource[imageNumber]);
Uri src = new Uri(sUri, UriKind.Relative);
var bmp = new BitmapImage(src);
img.Source = bmp;
}
xaml
<Image x:Name="img">
<Image.Source>
<BitmapImage UriSource="Resources/BlackJackTut-1.jpg" />
</Image.Source>
</Image>
In WPF application you can do same also with "pack://application:,,,/resources/imagename.png".
This way called Pack URI. This is static, but with these code you can do same an even use resource ;)
Put image in Resources.
private BitmapImage ConvertBitmapToBitmapImage(System.Drawing.Bitmap bitmap)
{
MemoryStream memoryStream = new MemoryStream();
bitmap.Save(memoryStream, System.Drawing.Imaging.ImageFormat.Png);
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = new MemoryStream(memoryStream.ToArray());
bitmapImage.EndInit();
return bitmapImage;
}
and then use this:
private void btn_Click(object sender, RoutedEventArgs e)
{
this.Img.Source = ConvertBitmapToBitmapImage(Properties.Resources.iamge1);
}
Just make an Image outside the mainWindow.
Then:
myImageOnScreen.Source = myImageOffScreen.Source;

Categories