1

I have a Windows 8 store app in XAML. I have an Image control that binds to a property, but because the URL I want to display requires authentication, I have to create a webresponse and generate the bitmap that way instead of just providing a URL to my image control.

Problem is, the memorystream operations are async, and properties on an object have to be sync, not async. So I have a pretty simple setup:

public ImageSource ImageSource
{
    get { return Task.Run(() => BitmapImageUtils.ToImage(this.Upload.ThumbFile)).Result; }

and the Image control has the ImageSource property as its binding. Problem is, I'm receiving the below exception. there's multiple Image controls in a ListView and they're all binding in this way, and my guess is that the UI thread that invokes this somehow hands off control to a thread then tries to come back somehow. I'm a little new to this.

The application called an interface that was marshalled for a different thread. (Exception from HRESULT: 0x8001010E (RPC_E_WRONG_THREAD))

Any help is appreciated.

--- EDIT ToImage Method

public static async Task<BitmapImage> ToImage(byte[] byteArray)
    {
        var bitmapImage = new BitmapImage();

        var stream = new InMemoryRandomAccessStream();
        await stream.WriteAsync(byteArray.AsBuffer());
        stream.Seek(0);

        await bitmapImage.SetSourceAsync(stream);
        return bitmapImage;
    }

Exception (Note the exception is an inner exception of an aggregate exception, which I believe is pretty standard for async/await exceptions

at Windows.UI.Xaml.Media.Imaging.BitmapImage..ctor()
at Campfire.Utils.BitmapImageUtils.<ToImage>d__0.MoveNext()
Richthofen
  • 2,076
  • 19
  • 39
  • 2
    Side note: property that executes network operation feels very wrong... What if it will be called more often than you expect? – Alexei Levenkov Dec 29 '13 at 18:49
  • Agreed, its not ideal. But I can't think of a way to show an image control on demand when the image is behind an authentication barrier. (requires a special header in the web request with an auth token). Plus in a listview the image isn't loaded until the item is in view, which in my case performs pretty well. – Richthofen Dec 29 '13 at 19:03
  • Side note: check out this question as it may be useful - [Make WPF Image load async](http://stackoverflow.com/questions/16035300/make-wpf-image-load-async). – Alexei Levenkov Dec 29 '13 at 19:07
  • It looks like the isasync won't work in Windows RT – Richthofen Dec 29 '13 at 19:14
  • What is the stack trace of the exception? And the code on ToImage might be useful too. – svick Dec 29 '13 at 20:30
  • Updated. Note also that 'ThumbFile' in this case is itself a synchronous call to an async method that does the WebRequest and retrieves a byte array. In the stack trace the byte array is not null and when I move the call to ThumbFile it retrieves the byte array correctly. – Richthofen Dec 29 '13 at 23:32

2 Answers2

4

The core problem is that many UI types (including BitmapImage) have UI thread affinity, and your current code is trying to create them on a background thread (in Task.Run).

I recommend that you redesign your property; you shouldn't have it blocking on an I/O operation anyway. I have a type in my AsyncEx library that allows you to essentially data-bind to the results of a Task<T>.

Your property becomes:

public INotifyTaskCompletion<ImageSource> ImageSource { get; private set; }

And when you want to start downloading, you do:

ImageSource = NotifyTaskCompletion.Create(BitmapImageUtils.ToImage(Upload.ThumbFile)));

Your databinding then changes from ImageSource to ImageSource.Result. There are other properties as well (e.g., ImageSource.IsFaulted, ImageSource.ErrorMessage) that allow you to handle other results with data binding. Note that INotifyTaskCompletion<T>.Result is not blocking; it will just return the default value until the task completes.

P.S. AggregateException is not normal in async/await. The only reason you were seeing it is because you were using Task<T>.Result; if you await the same task, you'll get an exception that is not wrapped in AggregateException.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
0

Why not add a LoadImage to the object with the property on it, and get that to do the loading in an async manner. Once it's finished loading everything it can set the ImageSource property normally.

Ross Dargan
  • 5,876
  • 4
  • 40
  • 53
  • I'm not sure I grok what you're saying. Is 'LoadImage' a method in your example? If so that doesn't solve the problem as encapsulating in another method would still yield this error. – Richthofen Dec 29 '13 at 19:06
  • I don't think it would. I have had this happened to me when I have tried to hack in things like that. I tend to have an init method on my windows 8 VM's that do all this loading for me. – Ross Dargan Dec 29 '13 at 19:32