Update WPF UI from Different Thread VB.net [duplicate] - wpf

Here's my problem.
I'm loading a few BitmapImages in a BlockingCollection
public void blockingProducer(BitmapImage imgBSource)
{
if (!collection.IsAddingCompleted)
collection.Add(imgBSource);
}
the loading happens in a backgroungwork thread.
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
String filepath; int imgCount = 0;
for (int i = 1; i < 10; i++)
{
imgCount++;
filepath = "Snap";
filepath += imgCount;
filepath += ".bmp";
this.Dispatcher.BeginInvoke(new Action(() =>
{
label1.Content = "Snap" + imgCount + " loaded.";
}), DispatcherPriority.Normal);
BitmapImage imgSource = new BitmapImage();
imgSource.BeginInit();
imgSource.UriSource = new Uri(filepath, UriKind.Relative);
imgSource.CacheOption = BitmapCacheOption.OnLoad;
imgSource.EndInit();
blockingProducer(imgSource);
}
}
debugging this part of the code everything looks okay, the problem comes now ...
after finishing loading the images I want to show them in UI one by one. I'm using a dispatcher to do so but I always get the message telling me that the called Thread can not access the object because it belongs to a different Thread.
public void display(BlockingCollection<BitmapImage> results)
{
foreach (BitmapImage item in collection.GetConsumingEnumerable())
{
this.Dispatcher.BeginInvoke(new Action(() =>
{
this.dstSource.Source = item;
Thread.Sleep(250);
}), DispatcherPriority.Background);
}
}
debug accuses that the error is here
this.dstSource.Source = item;
I'm trying everything but cant find out what’s wrong. Anyone has any idea?

You have to call Freeze after loading the images in order to make them accessible to other threads:
BitmapImage imgSource = new BitmapImage();
imgSource.BeginInit();
imgSource.UriSource = new Uri(filepath, UriKind.Relative);
imgSource.CacheOption = BitmapCacheOption.OnLoad;
imgSource.EndInit();
imgSource.Freeze(); // here
As far as I have understood the BitmapCacheOption.OnLoad flag, it is only effective when a BitmapImage is loaded from a stream. The Remarks section in BitmapCacheOption says:
Set the CacheOption to BitmapCacheOption.OnLoad if you wish to close a
stream used to create the BitmapImage. The default OnDemand cache
option retains access to the stream until the image is needed, and
cleanup is handled by the garbage collector.
A BitmapImage created from a Uri may be loaded asynchronously (see the IsDownloading property). Consequently, Freeze may not be callable on such a BitmapImage, as downloading may still be in progress after EndInit. I guess it nevertheless works in your case because you are loading BitmapImages from file Uris, which seems to be done immediately.
To avoid this potential problem you may just create the BitmapImage from a FileStream:
var imgSource = new BitmapImage();
using (var stream = new FileStream(filepath, FileMode.Open))
{
imgSource.BeginInit();
imgSource.StreamSource = stream;
imgSource.CacheOption = BitmapCacheOption.OnLoad;
imgSource.EndInit();
imgSource.Freeze();
}

For the further future readers, here is the code I used to fix my problem.
public void display(BlockingCollection<BitmapImage> collection)
{
if (collection.IsCompleted || collection.Count != 0)
{
BitmapImage item = collection.Take();
this.Dispatcher.BeginInvoke(new Action(() =>
{
this.dstSource.Source = item;
}), DispatcherPriority.Normal);
}
else
{
dispatcherTimer.Stop();
}
}
public void dispatcherTimer_Tick(object sender, EventArgs e)
{
display(collection);
}
public void configureDispatcherTimer()
{
dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
TimeSpan interval = TimeSpan.FromMilliseconds(150);
dispatcherTimer.Interval = interval;
}

Related

Picturebox porting to WPF [duplicate]

This question already has answers here:
Using BitmapImage created on Threadpool thread
(1 answer)
InvalidOperationException delegation UI Thread C#
(1 answer)
Closed 3 years ago.
I'm having a lot of pain porting my picturebox from Windows Form to WPF.
My problem is not about the picturebox itself, but I'm having troubles displaying an image gotten from NetworkStream from a Thread into an Image in WPF.
I got this NetworkStream from a TcpClient connection client-server.
This is my Windows Form code (relevant part):
Thread getImage;
void StartReceiving(){
getImage = new Thread(ReceiveImage);
getImage.Start();
}
private void ReceiveImage()
{
BinaryFormatter binaryFormatter = new BinaryFormatter();
while (client.Connected)
{
mainStream = client.GetStream();
try
{
pictureBox1.Image = (Image)binaryFormatter.Deserialize(mainStream);
}
catch (Exception ex) { }
}
}
This code works, I get image in loop every 100 ms and I need to update that source.
In WPF I tried with an Image and setting it's source (I tried both UriSource and StreamSource) but no success. I got thread exceptions, "nothing appens" errors, ...
This is my last code, it results into a Thread error but I don't know what else to try to make it thread compliant. (I only posted the relevant part)
private readonly Thread getImage;
public SecondWindow(int port)
{
InitializeComponent();
client = new TcpClient();
getImage = new Thread(new ThreadStart(ReceiveImage));
while (!client.Connected)
{
server.Start();
client = server.AcceptTcpClient();
}
getImage.Start();
}
private void ReceiveImage()
{
BinaryFormatter binaryFormatter = new BinaryFormatter();
BitmapImage imgSrc = new BitmapImage();
while (client.Connected)
{
var mainStream = client.GetStream();
int loop = 0;
while (!mainStream.DataAvailable && loop < 500)
{
loop++;
Thread.Sleep(10);
}
if (mainStream.DataAvailable)
{
try
{
imgSrc = new BitmapImage();
imgSrc.BeginInit();
imgSrc.StreamSource = mainStream;
imgSrc.CacheOption = BitmapCacheOption.OnLoad;
imgSrc.EndInit();
if (imgSrc.CanFreeze && !imgSrc.IsFrozen)
imgSrc.Freeze();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
if (displayImage.Dispatcher.CheckAccess())
{
displayImage.Source = imgSrc;
}
else
{
Action act = () => { displayImage.Source = imgSrc; };
displayImage.Dispatcher.BeginInvoke(act);
}
}
}
}
In the above code I have 2 problems: 1st that imgSrc can never freeze
2nd (probably a direct consequence of 1st problem), I get InvalidOperationException The calling thread cannot access this object because a different thread owns it
Thanks all for the support
I solved!! the solution was using some other methods in order to deserialize the full stream and then apply it to the image source!
This might not be the best solution but this answer pointed me to the end of this agony
This is my working code:
[DllImport("gdi32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool DeleteObject(IntPtr value);
private void ReceiveImage()
{
BinaryFormatter binaryFormatter = new BinaryFormatter();
System.Drawing.Image imgSrc;
while (client.Connected)
{
var mainStream = client.GetStream();
int loop = 0;
while (!mainStream.DataAvailable && loop < 500)
{
loop++;
Thread.Sleep(10);
}
if (mainStream.DataAvailable)
{
try
{
imgSrc = (System.Drawing.Image)binaryFormatter.Deserialize(mainStream);
var bitmap = new Bitmap(imgSrc);
IntPtr bmpPt = bitmap.GetHbitmap();
BitmapSource bitmapSource =
System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
bmpPt,
IntPtr.Zero,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
//freeze bitmapSource and clear memory to avoid memory leaks
if (bitmapSource.CanFreeze && !bitmapSource.IsFrozen)
bitmapSource.Freeze();
DeleteObject(bmpPt);
if (displayImage.Dispatcher.CheckAccess())
{
displayImage.Source = bitmapSource;
}
else
{
Action act = () => { displayImage.Source = bitmapSource; };
displayImage.Dispatcher.BeginInvoke(act);
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
}
}

Async Program still freezing up the UI

Hello I'm writing a WPF program that gets has thumbnails inside a ThumbnailViewer. I want to generate the Thumbnails first, then asynchronously generate the images for each thumbnail.
I can't include everything but I think this is whats relevant
Method to generate the thumbnails.
public async void GenerateThumbnails()
{
// In short there is 120 thumbnails I will load.
string path = #"C:\....\...\...png";
int pageCount = 120;
SetThumbnails(path, pageCount);
await Task.Run(() => GetImages(path, pageCount);
}
SetThumbnails(string path, int pageCount)
{
for(int i = 1; i <= pageCount; i ++)
{
// Sets the pageNumber of the current thumbnail
var thumb = new Thumbnail(i.ToString());
// Add the current thumb to my thumbs which is
// binded to the ui
this._viewModel.thumbs.Add(thumb);
}
}
GetImages(string path, int pageCount)
{
for(int i = 1; i <= pageCount; i ++)
{
Dispatcher.Invoke(() =>
{
var uri = new Uri(path);
var bitmap = new BitmapImage(uri);
this._viewModel.Thumbs[i - 1].img.Source = bitmap;
});
}
}
When I run the code above it works just as if I never add async/await/task to the code. Am I missing something? Again What I want is for the ui to stay open and the thumbnail images get populated as the GetImage runs. So I should see them one at a time.
UPDATE:
Thanks to #Peregrine for pointing me in the right direction. I made my UI with custom user controls using the MVVM pattern. In his answer he used it and suggested that I use my viewModel. So what I did is I add a string property to my viewModel and made an async method that loop though all the thumbnails and set my string property to the BitmapImage and databound my UI to that property. So anytime it would asynchronously update the property the UI would also update.
The Task that runs GetImages does virtually nothing but Dispatcher.Invoke, i.e. more or less all your code runs in the UI thread.
Change it so that the BitmapImage is created outside the UI thread, then freeze it to make it cross-thread accessible:
private void GetImages(string path, int pageCount)
{
for (int i = 0; i < pageCount; i++)
{
var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.UriSource = new Uri(path);
bitmap.EndInit();
bitmap.Freeze();
Dispatcher.Invoke(() => this._viewModel.Thumbs[i].img.Source = bitmap);
}
}
You should also avoid any async void method, excpet when it is an event handler. Change it as shown below, and await it when you call it:
public async Task GenerateThumbnails()
{
...
await Task.Run(() => GetImages(path, pageCount));
}
or just:
public Task GenerateThumbnails()
{
...
return Task.Run(() => GetImages(path, pageCount));
}
An alternative that altogether avoids async/await is a view model with an ImageSource property whose getter is called asynchronously by specifying IsAsync on the Binding:
<Image Source="{Binding Image, IsAsync=True}"/>
with a view model like this:
public class ThumbnailViewModel
{
public ThumbnailViewModel(string path)
{
Path = path;
}
public string Path { get; }
private BitmapImage îmage;
public BitmapImage Image
{
get
{
if (îmage == null)
{
îmage = new BitmapImage();
îmage.BeginInit();
îmage.CacheOption = BitmapCacheOption.OnLoad;
îmage.UriSource = new Uri(Path);
îmage.EndInit();
îmage.Freeze();
}
return îmage;
}
}
}
It looks as though you've been mislead by the constructor of BitmapImage that can take a Url.
If this operation really is slow enough to justify using the async-await pattern, then you would be much better off dividing it into two sections.
a) Fetching the data from the url. This is the slow part - it's IO bound, and would benefit most from async-await.
public static class MyIOAsync
{
public static async Task<byte[]> GetBytesFromUrlAsync(string url)
{
using (var httpClient = new HttpClient())
{
return await httpClient
.GetByteArrayAsync(url)
.ConfigureAwait(false);
}
}
}
b) Creating the bitmap object. This needs to happen on the main UI thread, and as it's relatively quick anyway, there's no gain in using async-await for this part.
Assuming that you're following the MVVM pattern, you shouldn't have any visual elements in the ViewModel layer - instead use a ImageItemVm for each thumbnail required
public class ImageItemVm : ViewModelBase
{
public ThumbnailItemVm(string url)
{
Url = url;
}
public string Url { get; }
private bool _fetchingBytes;
private byte[] _imageBytes;
public byte[] ImageBytes
{
get
{
if (_imageBytes != null || _fetchingBytes)
return _imageBytes;
// refresh ImageBytes once the data fetching task has completed OK
Action<Task<byte[]>> continuation = async task =>
{
_imageBytes = await task;
RaisePropertyChanged(nameof(ImageBytes));
};
// no need for await here as the continuations will handle everything
MyIOAsync.GetBytesFromUrlAsync(Url)
.ContinueWith(continuation,
TaskContinuationOptions.OnlyOnRanToCompletion)
.ContinueWith(_ => _fetchingBytes = false)
.ConfigureAwait(false);
return null;
}
}
}
You can then bind the source property of an Image control to the ImageBytes property of the corresponding ImageItemVm - WPF will automatically handle the conversion from byte array to a bitmap image.
Edit
I misread the original question, but the principle still applies. My code would probably still work if you made a url starting file:// but I doubt it would be the most efficient.
To use a local image file, replace the call to GetBytesFromUrlAsync() with this
public static async Task<byte[]> ReadBytesFromFileAsync(string fileName)
{
using (var file = new FileStream(fileName,
FileMode.Open,
FileAccess.Read,
FileShare.Read,
4096,
useAsync: true))
{
var bytes = new byte[file.Length];
await file.ReadAsync(bytes, 0, (int)file.Length)
.ConfigureAwait(false);
return bytes;
}
}
Rather than involving the the dispatcher and jumping back and forth, I'd do something like this:
private Task<BitmapImage[]> GetImagesAsync(string path, int pageCount)
{
return Task.Run(() =>
{
var images = new BitmapImage[pageCount];
for (int i = 0; i < pageCount; i++)
{
var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.UriSource = new Uri(path);
bitmap.EndInit();
bitmap.Freeze();
images[i] = bitmap;
}
return images;
}
}
Then, on the UI thread calling code:
var images = await GetImagesAsync(path, pageCount);
for (int i = 0; i < pageCount; i++)
{
this._viewModel.Thumbs[i].img.Source = images[i];
}

Loading Images in background while not locking the files

My application loads a lot of images in a BackgroundWorker to stay usable. My Image control is bound to a property named "ImageSource". If this is null it's loaded in the background and raised again.
public ImageSource ImageSource
{
get
{
if (imageSource != null)
{
return imageSource;
}
if (!backgroundImageLoadWorker.IsBusy)
{
backgroundImageLoadWorker.DoWork += new DoWorkEventHandler(bw_DoWork);
backgroundImageLoadWorker.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
backgroundImageLoadWorker.RunWorkerAsync();
}
return imageSource;
}
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
bitmap = new BitmapImage();
bitmap.BeginInit();
try
{
bitmap.CreateOptions = BitmapCreateOptions.DelayCreation;
bitmap.DecodePixelWidth = 300;
MemoryStream memoryStream = new MemoryStream();
byte[] fileContent = File.ReadAllBytes(imagePath);
memoryStream.Write(fileContent, 0, fileContent.Length);
memoryStream.Position = 0;
bitmap.StreamSource = memoryStream;
}
finally
{
bitmap.EndInit();
}
bitmap.Freeze();
e.Result = bitmap;
}
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
BitmapSource bitmap = e.Result as BitmapSource;
if (bitmap != null)
{
Dispatcher.CurrentDispatcher.BeginInvoke(
(ThreadStart)delegate()
{
imageSource = bitmap;
RaisePropertyChanged("ImageSource");
}, DispatcherPriority.Normal);
}
}
This is all well so far but my users can change the images in question. They choose a new image in an OpenDialog, the old image file is overwritten with the new and ImageSource is raised again which loads the new image with the same filename again:
public string ImagePath
{
get { return imagePath; }
set
{
imagePath= value;
imageSource = null;
RaisePropertyChanged("ImageSource");
}
}
On some systems the overwriting of the old file results in an exception:
"a generic error occured in GDI+" and "The process cannot access the file..."
I tried a lot of things like loading with BitmapCreateOptions.IgnoreImageCache and BitmapCacheOption.OnLoad. This raised Exceptions when loading them:
Key cannot be null.
Parameter name: key
If I try this without the BackgroundWorker on the UI thread it works fine. Am I doing something wrong? Isn't it possible to load the images in the background while keeping the files unlocked?
Well, it seems all of the above works. I simplified the example for the question and somehow on the way lost the problem.
The only difference I could see in my code is that the loading of the image itself was delegated to a specific image loader class which somehow created the problem. When I removed this dependency the errors disappeared.

WPF STA thread error

I am following the code given on this thread C# Async WebRequests: Perform Action When All Requests Are Completed
In my WPF app I need to asynchronously download images from the server. However I get the following error
The calling thread must be STA, because many UI components require this.
Could it be because I am doing UI updates on the main thread? I have also declared the calling thread's state to STA, my code follows:
private void FixedDocument_Loaded(object sender, RoutedEventArgs e)
{
Thread t = new Thread(new ThreadStart(AsyncLoadImages));
t.IsBackground = true;
t.SetApartmentState(ApartmentState.STA);
t.Start();
t.Join();
}
private void AsyncLoadImages()
{
foreach (string resFile in resFiles)
{
string imageuri = #"http://www.example.com/image.jpg";
WebRequest request = HttpWebRequest.Create(imageuri);
request.Method = "GET";
object data = new object();
RequestState state = new RequestState(request, data);
IAsyncResult result = request.BeginGetResponse(
new AsyncCallback(UpdateItem), state);
ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle, new WaitOrTimerCallback(ScanTimeoutCallback), state, (30 * 1000), true);
}
}
private static void ScanTimeoutCallback(object state, bool timedOut)
{
if (timedOut)
{
RequestState reqState = (RequestState)state;
if (reqState != null)
{
reqState.Request.Abort();
}
Console.WriteLine("aborted- timeout");
}
}
private void UpdateItem(IAsyncResult result)
{
RequestState state = (RequestState)result.AsyncState;
WebRequest request = (WebRequest)state.Request;
HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(result);
BitmapImage bi = new BitmapImage();
bi.BeginInit();
bi.StreamSource = response.GetResponseStream();
bi.EndInit();
Image i = new Image(); //hitting the error at this line
i.Source = bi;
}
Please can someone help?
Many Thanks
You can try wrapping your code in below, this however is dirty solution.
MyUIElement.Dispatcher.Invoke(DispatcherPriority.Normal, (Action)(() =>
{
//your code here
}));
best if MyUIElement was your top window.
You need to call every UI operation in the MainThread i guess your UpdateItem method will not be called in the UI Thread thus you get this exception.
i would change 2 things:
First, use the BackgroundWorker class, which makes this kind of async operations in WPF alot simpler.
Second, if you have another thread (Backgroundworker or custom Thread) you always must Dispatch every UI operation into the main thread.

BitmapFrame in another thread

I am using a WPF BackgroundWorker to create thumbnails. My worker function looks like:
private void work(object sender, DoWorkEventArgs e)
{
try
{
var paths = e.Argument as string[];
var boxList = new List<BoxItem>();
foreach (string path in paths)
{
if (!string.IsNullOrEmpty(path))
{
FileInfo info = new FileInfo(path);
if (info.Exists && info.Length > 0)
{
BitmapImage bi = new BitmapImage();
bi.BeginInit();
bi.DecodePixelWidth = 200;
bi.CacheOption = BitmapCacheOption.OnLoad;
bi.UriSource = new Uri(info.FullName);
bi.EndInit();
var item = new BoxItem();
item.FilePath = path;
MemoryStream ms = new MemoryStream();
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bi));
encoder.Save(ms);
item.ThumbNail = ms.ToArray();
ms.Close();
boxList.Add(item);
}
}
}
e.Result = boxList;
}
catch (Exception ex)
{
//nerver comes here
}
}
When this function is finished and before the BackgroundWorker "Completed" function is started, I can see on the output window on Vs2008, that a exception is generated. It looks like:
A first chance exception of type 'System.NotSupportedException' occurred in PresentationCore.dll
The number of exceptions generates equals the number of thumbnails to be generated.
Using "trial and error" method I have isolated the problem to:
BitmapFrame.Create(bi)
Removing that line (makes my function useless) also removes the exception.
I have not found any explanation to this, or a better method to create thumbnails in a background thread.
Lasse, I believe the problem arises because you are performing actions outside of the UI thread that need to be done within the UI thread. Creating UI elements (BitmapImage, BitmapFrame) and adding to UI Containers, I believe, should be done on the UI thread. (Someone correct me if I'm wrong here).
There are a few ways to create those elements on the UI thread without blocking the application for an excessive period of time. The easiest is probably using the BackgroundWorker's ProgressChanged event. ProgressChanged is invoked on the UI thread, which makes it perfect for this situation.
You can use the worker's ProgressChanged event and pass it the path needed to load a thumbnail in the UserState argument.
Thanks for your input. It made start to look for another solutions and I come up with this.
try
{
var paths = e.Argument as string[];
var boxList = new List<BoxItem>();
foreach (string path in paths)
{
using (Image photoImg = Image.FromFile(path))
{
int newWidth = 200;
int width = newWidth;
int height = (photoImg.Height * newWidth) / photoImg.Width;
var thumbnail = new Bitmap(width, height);
using (Graphics g = Graphics.FromImage((System.Drawing.Image)thumbnail))
{
g.DrawImage(photoImg, 0, 0, width, height);
using (var ms = new System.IO.MemoryStream())
{
var item = new BoxItem();
item.FilePath = path;
thumbnail.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
item.ThumbNail = ms.ToArray();
boxList.Add(item);
}
}
}
}
e.Result = boxList;
}
catch (Exception exp)
{
}
Not using any UI elements .. and works nicely.
Thanks.
//lasse

Resources