Tuesday, January 22, 2013

Synchronously get picture (from Uri)

Synchronously get picture (from Uri)

At the execution of my application, I need to know if a picture (from a specified URI) is available before I decide if I take the live picture or a "is unavailable picture".

Because the picture is only set once, I need to know synchronously if the picture is accessible and not corrupted. I tried with the Reactive Extensions, but I have never been able to get the first element ( the whole application freezes when on the .First() statement)

The following blogger covered my issue but his code was not even compiling. I guess it may be related to a new syntax use by Reactive. Furthermore, Reactive is still in beta with Silverlight 5 and I guess it may still have unusual behaviours.

I am open to any other possible solution, but the better solution for me will be to be able to Create a OpenRead() extension methods to the Webclient class, and using it like this :

var pictureStream = new WebClient().OpenRead(_uri); if (pictureStream != null) {     var picture = new BitmapImage();     picture.SetSource(picture);     return picture; } else {     //Picture is unavailable } 

Thank you very much!

Answers & Comments...

Answer: 1

The mistake some make is imagining that they can leave unmodified their synchronous code (in your case code that expects to be able to call a function which returns a picture) and somehow just make a part of it cope with being asynchronous. This just isn't possible, as soon as a function your code is calling needs to be asynchronous then so does the calling code itself and so does the code that calls that and so on all the way up to whatever event initiated the code.

New features of the C# language on the horizon are designed to ease this situation but they are not available to you right now. It would still require all code in the chain to understand that it is asynchronous.

I often see Reactive touted as the solution to this sort of issue, however, Reactive isn't really designed to solve this issue and you end up jumping through some ugly hoops trying to get it work.

I offer therefore the solution that I use as blogged here. With this code in place (and it requires a surprisingly short amount of code added to a project) your code can be written as:-

  IEnumerable<AsyncOperation> GetPicture(Uri uri, Action<ImageSource> returnResult)   {         WebRequest req = WebRequest.Create(uri)          Stream pictureStream = null;         yield return req.GetRequestStreamAsyncOp(r =>         {              try {reqStream = r; } catch { }         });          yield return AsyncOperationService.SwitchToUIThread();          if (pictureStream != null)          {              var picture = new BitmapImage();              picture.SetSource(picture);              returnResult(picture);          }          else          {               //Picture is unavailable              //returnResult(someOtherPicure);         }     } 

You calling code would look like this:-

 GetPicture(_uri, picture =>  {        //Do stuff with your picture here.   }).Run(err =>  {      if (err != null)      {          //Oops something bad happened code here.      }  }); 
by : AnthonyWJoneshttp://stackoverflow.com/users/17516

Answer: 2

Rx is just fine for this type of thing. I believe the question is wrong, you actually want to be Async. You just want a continuation from the the result of the Web Request. Rx can do this just fine.

var _uri = @"http://blog.stackoverflow.com/wp-content/uploads/stackoverflow-sticker-proof.png"; var prictureRequest = Observable.Start<BitmapImage>(()=> {     var pictureStream = new WebClient().OpenRead(_uri);      if (pictureStream != null)      {              var picture = new BitmapImage();              picture.StreamSource = pictureStream;              return picture;      }      else      {              //Picture is unavailable          //maybe throw new InvalidOperationException("Not valid image");     //or      return ImageCache.PictureUnavailable;     }  }); 

Then you subscribe to the request.

var subscription = pictureRequest.Subscribe(     img=>   Console.WriteLine ("Set image here eg. MyImage.Source = img"),     ex=>    Console.WriteLine ("Fail!. Do something about exception here")     ); 

You then just need to ensure that you subscribe on another thread (like by using the thread pool) and ensure you update on the UI thread. We should already be using the ThreadPool as we have used Observable.Start to get us into Rx.

var subscription = pictureRequest     //.SubscribeOn(Scheduler.ThreadPool)//Not needed as Observable.Start defaults to this.     .ObserveOnDispatcher()     .Subscribe(         img=>   Console.WriteLine ("Set image here eg. MyImage.Source = img"),         ex=>    Console.WriteLine ("Fail!. Do something about exceptio here")     ); 

This will work on WPF code. I cant remember off the top of my head if the SubscribeOn( ThreadPool)/Observable.Start will work or not with SL's rules on IO (ie with a WebRequest). If not then I would think APM is what you want anyway.

Check out my blog for some more stuff on Rx, Async and downloading images (WPF).

http://leecampbell.blogspot.com/2011/05/rx-code-from-perth-presentation.html

Also there I have a blog series that may help you with a better understanding of Rx (like dont use .First())

http://leecampbell.blogspot.com/2010/08/reactive-extensions-for-net.html

by : Lee Campbellhttp://stackoverflow.com/users/393615

Answer: 3

The Silverlight version (had to install it on the new PC).

Assuming you have defined a const or "static readonly" instance of the missing image to show, this is your bit of business logic

private static BitmapImage ToBitmapImage(Stream pictureStream) {     if (pictureStream != null)     {         var picture = new BitmapImage();         picture.SetSource(pictureStream);         return picture;     }     return _missingImage; } 

Then you can have a method to get the Image with Rx, but using the SL WebClient under the hood.

private static IObservable<BitmapImage> GetImage(string path) {     var uri = new Uri(path);      return Observable.Create<BitmapImage>(o=>     {         var webClient = new WebClient();         var request = Disposable.Create(webClient.CancelAsync);         var readComplete = Observable.FromEventPattern<OpenReadCompletedEventHandler, OpenReadCompletedEventArgs>(             h => webClient.OpenReadCompleted += h,             h => webClient.OpenReadCompleted -= h);          var subscription = readComplete             .Select(e => ToBitmapImage(e.EventArgs.Result))             .Subscribe(o);         webClient.OpenReadAsync(uri);         return new CompositeDisposable(request, subscription);                }); } 

Great news is that SL does all the threading for you so no shcedulers are required and everything stays nice and responsive (ie the UI does not freeze).

Is this what you are looking for?

by : Lee Campbellhttp://stackoverflow.com/users/393615




No comments:

Post a Comment

Send us your comment related to the topic mentioned on the blog