Reputation: 43
I have a strange behavior in WPF 4.5 (.net 4.5). I'm using the keywords await and async in order to run long-operations (for example load a big BitmapImage, base for a Image control). The problem is that the awaiter doesn't return to the main UI Thread, because I get teh famous Exception:
The calling thread cannot access this object because a different thread owns it.
Could someone help me?
Here my code:
Event handler of a button:
private void GetExifData_Click(object sender, RoutedEventArgs e)
{
// Async method
(new AnalyzeSingleImage()).RunExif(this);
}
The main method (in a separate class, same assembly)
public async void RunExif(MainWindow win)
{
// here I run correctly code on the main UI Thread
..
..
// ASYNC !!!
BitmapImage bi = await LoadImageAsync(fileName);
Image img = new Image();
img.Source = bi; // *********** HERE I GET THE EXCEPTION *************
..
..
}
The Async method:
private Task<BitmapImage> LoadImageAsync(string fileName)
{
return Task<BitmapImage>.Run(() => LoadImage(fileName));
}
The long time method:
private BitmapImage LoadImage(string fileName)
{
BitmapImage bi = new BitmapImage();
bi.BeginInit();
bi.UriSource = new Uri(fileName);
bi.CacheOption = BitmapCacheOption.OnLoad;
bi.EndInit();
return bi;
}
Someone could help me please?
Upvotes: 2
Views: 437
Reputation: 43
at the end I used this approach:
On the main UI
BitmapImage bi = await LoadImageAsync(fileName);
Image img = new Image();
img.Source = bi;
borderImg.Child = img;
Long task..
private Task<BitmapImage> LoadImageAsync(string fileName)
{
return Task<BitmapImage>.Run(() => LoadImage(fileName));
}
The trick is use the Freeze member!!
private BitmapImage LoadImage(string fileName)
{
BitmapImage bi = new BitmapImage();
bi.BeginInit();
//bi.StreamSource = imgAsStream;
bi.UriSource = new Uri(fileName);
bi.CacheOption = BitmapCacheOption.OnLoad;
bi.DecodePixelWidth = 400; // riduce l'utilizzo di memoria!
bi.EndInit();
bi.Freeze(); // IMPORTANTE, ALTRIMENTI RITORNANDO ALLA UI Thread ho un errore!!!!
return bi;
}
Upvotes: 1
Reputation: 171178
LoadImage
runs on the thread-pool because you pushed it there using Task.Run
. It is valid to use UI elements on different threads but you must access them on the same thread consistently.
The BitmapImage
you created is now bound to a thread-pool thread. It cannot be combined with the main UI.
For that reason, it is best practice to only have UI on a single thread. Create the bitmap on the main thread.
If I remember WPF correctly creating a BitmapImage
is very quick and loading the image is async anyway.
Upvotes: 2