同步获取图片(来自Uri)

时间:2012-02-12 22:41:11

标签: silverlight webclient system.reactive bitmapimage silverlight-5.0

在执行我的应用程序时,我需要知道在决定是拍摄实时照片还是“不可用照片”之前是否有照片(来自指定的URI)。

因为图片只设置了一次,所以我需要同步知道图片是否可访问且未损坏。我尝试使用Reactive Extensions,但我从来没有能够获得第一个元素(整个应用程序在.First()语句时冻结)

以下blogger 涵盖了我的问题,但他的代码甚至没有编译。我想这可能与Reactive的新语法使用有关。此外,Reactive仍然处于Silverlight 5的测试阶段,我猜它可能仍有异常行为。

我对任何其他可能的解决方案持开放态度,但对我来说更好的解决方案是能够为Webclient类创建一个OpenRead()扩展方法,并像这样使用它:

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

非常感谢!

3 个答案:

答案 0 :(得分:0)

有些人犯的错误是想象他们可以保留未经修改的同步代码(在您的情况下代码中,希望能够调用返回图片的函数)并且不知何故只是做一部分它应对异步。这是不可能的,只要您的代码调用的函数需要异步,调用代码本身也是如此,调用它的代码也是如此,一直到发起代码的任何事件。

即将推出的C#语言的新功能旨在缓解这种情况,但目前无法使用它们。它仍然需要链中的所有代码才能理解它是异步的。

我经常看到Reactive被吹捧为这类问题的解决方案,但是,Reactive并不是真正意图解决这个问题,而是最终会跳过一些丑陋的箍,试图让它发挥作用。

因此,我提供了我用作博客here的解决方案。使用此代码(并且需要将少量代码添加到项目中),您的代码可以写成: -

  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);
        } 

  }

您调用代码的方式如下: -

 GetPicture(_uri, picture =>
 {

      //Do stuff with your picture here.

 }).Run(err =>
 {
     if (err != null)
     {
         //Oops something bad happened code here.
     }
 });

答案 1 :(得分:0)

Rx适用于此类事情。我认为问题是错的,你实际上想要成为异步。您只想从Web请求的结果继续。 Rx可以做到这一点。

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;
    } 
});

然后您订阅了请求。

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

然后,您只需确保在另一个线程上订阅(例如使用线程池)并确保在UI线程上进行更新。我们应该已经使用了ThreadPool,因为我们已经使用Observable.Start将我们带入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")
    );

这将适用于WPF代码。如果SubscribeOn(ThreadPool)/ Observable.Start与SL的IO规则(即使用WebRequest)一起工作或不工作,我无法记住我的头脑。如果没有,那么我认为APM无论如何都是你想要的。

查看我的博客,了解更多有关Rx,Async和下载图像(WPF)的内容。

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

此外,我还有一个博客系列可以帮助您更好地理解Rx(比如不要使用.First())

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

答案 2 :(得分:0)

Silverlight版本(必须在新PC上安装)。

假设您已经定义了要显示的缺失图像的const或“static readonly”实例,这是您的业务逻辑

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

然后你可以有一个方法来获取带有Rx的Image,但是使用SL WebClient。

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);           
    });
}

好消息是SL为您完成所有线程,因此不需要任何shceduler,一切都保持良好和响应(即UI不会冻结)。

这是你在找什么?