There are a bunch of questions about this already on this site and other forums, but I\'ve yet to find a solution that actually works.
Here\'s what I want to do:
The BitmapImage needs async support for all of its events and internals. Calling Dispatcher.Run() on the background thread will...well run the dispatcher for the thread. (BitmapImage inherits from DispatcherObject so it needs a dispatcher. If the thread that created the BitmapImage doesn't already have a dispatcher a new one will be created on demand. cool.).
Important safety tip: The BitmapImage will NOT raise any events if it is pulling data from cache (rats).
This has been working very well for me....
var worker = new BackgroundWorker() { WorkerReportsProgress = true };
// DoWork runs on a brackground thread...no thouchy uiy.
worker.DoWork += (sender, args) =>
{
var uri = args.Argument as Uri;
var image = new BitmapImage();
image.BeginInit();
image.DownloadProgress += (s, e) => worker.ReportProgress(e.Progress);
image.DownloadFailed += (s, e) => Dispatcher.CurrentDispatcher.InvokeShutdown();
image.DecodeFailed += (s, e) => Dispatcher.CurrentDispatcher.InvokeShutdown();
image.DownloadCompleted += (s, e) =>
{
image.Freeze();
args.Result = image;
Dispatcher.CurrentDispatcher.InvokeShutdown();
};
image.UriSource = uri;
image.EndInit();
// !!! if IsDownloading == false the image is cached and NO events will fire !!!
if (image.IsDownloading == false)
{
image.Freeze();
args.Result = image;
}
else
{
// block until InvokeShutdown() is called.
Dispatcher.Run();
}
};
// ProgressChanged runs on the UI thread
worker.ProgressChanged += (s, args) => progressBar.Value = args.ProgressPercentage;
// RunWorkerCompleted runs on the UI thread
worker.RunWorkerCompleted += (s, args) =>
{
if (args.Error == null)
{
uiImage.Source = args.Result as BitmapImage;
}
};
var imageUri = new Uri(@"http://farm6.static.flickr.com/5204/5275574073_1c5b004117_b.jpg");
worker.RunWorkerAsync(imageUri);