Silverlight 同步获取图片(从Uri)

Silverlight 同步获取图片(从Uri),silverlight,webclient,system.reactive,bitmapimage,silverlight-5.0,Silverlight,Webclient,System.reactive,Bitmapimage,Silverlight 5.0,在执行应用程序时,我需要知道指定URI中的图片是否可用,然后再决定是拍摄实时图片还是不可用图片 因为图片只设置了一次,所以我需要同步地知道图片是否可以访问并且没有损坏。我尝试了被动扩展,但始终无法获得在.first语句中整个应用程序冻结的第一个元素 下面介绍了我的问题,但他的代码甚至没有编译。我想这可能与被动语态的一种新语法有关。此外,Silverlight 5的Reactive仍处于测试阶段,我猜它可能仍有不寻常的行为 我愿意接受任何其他可能的解决方案,但对我来说更好的解决方案是能够为Webc

在执行应用程序时,我需要知道指定URI中的图片是否可用,然后再决定是拍摄实时图片还是不可用图片

因为图片只设置了一次,所以我需要同步地知道图片是否可以访问并且没有损坏。我尝试了被动扩展,但始终无法获得在.first语句中整个应用程序冻结的第一个元素

下面介绍了我的问题,但他的代码甚至没有编译。我想这可能与被动语态的一种新语法有关。此外,Silverlight 5的Reactive仍处于测试阶段,我猜它可能仍有不寻常的行为

我愿意接受任何其他可能的解决方案,但对我来说更好的解决方案是能够为Webclient类创建OpenRead扩展方法,并像这样使用它:

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

多谢各位

有些人犯的错误是想象他们可以在您的案例代码中保留未修改的同步代码,这些代码期望能够调用返回图片的函数,并以某种方式使其中的一部分能够处理异步。这是不可能的,只要代码调用的函数需要是异步的,那么调用代码本身也是异步的,调用该函数的代码也是异步的,一直到启动代码的事件

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

我经常看到人们吹捧“反应式”是解决这类问题的方法,然而,“反应式”并不是为了解决这类问题而设计的,你最终会跳出一些难看的圈套,试图让它发挥作用

因此,我提供了作为博客使用的解决方案。有了这段代码,并且需要在项目中添加的代码数量惊人地少,您的代码可以写成:-

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

  }

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;
    } 
});
然后,您只需要确保订阅另一个线程,就像使用线程池一样,并确保更新UI线程。我们应该已经使用了线程池,就像我们使用了Observable一样。开始让我们进入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、异步和下载WPF图像的内容

还有,我有一个博客系列,可以帮助你更好地理解Rx,比如不要使用


Silverlight版本必须安装在新电脑上

假设您已经定义了要显示的缺失图像的常量或静态只读实例,这就是您的业务逻辑

private static BitmapImage ToBitmapImage(Stream pictureStream)
{
    if (pictureStream != null)
    {
        var picture = new BitmapImage();
        picture.SetSource(pictureStream);
        return picture;
    }
    return _missingImage;
}
然后,您可以使用Rx获取图像,但在引擎盖下使用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不会冻结


这就是您想要的吗?

您可以简单地忘记Silverlight中的同步I/O调用。他们是不允许的。这是件好事。微软故意只保留异步版本的方法,以避免人们编写垃圾软件,而这些软件在某人执行I/O操作时会一直冻结。只需熟悉异步编程即可。在C 5.0中,将出现新的async/await关键字,允许您编写看起来同步的代码,但在幕后编译器会生成所有必要的管道来执行真正的异步非阻塞调用。我确实在考虑伪造异步调用,而这正是被动扩展的设计目的。。。所以我想这在某种程度上是可能的。我的整个应用程序都设计为异步的,但在这里,我真的需要知道当前函数中的信息。一个等待函数无疑会完成任务…@Philippe-Rx是关于抽象事件和异步操作的,而不是伪造它们。如果您曾经使用.First或它的姐妹阻塞函数,那么您可能没有做正确的事情。我建议你重新考虑你的方法。我不明白在你的代码中我从未调用WebService.OpenReadCompleted时,我该如何检索图片流。更重要的是,Silverlight中不存在WebService.OpenRead,这正是我想在扩展方法中抽象的函数。对不起,我已经创建了一个SL4版本,我认为它符合您的要求。见其他答案。
private static BitmapImage ToBitmapImage(Stream pictureStream)
{
    if (pictureStream != null)
    {
        var picture = new BitmapImage();
        picture.SetSource(pictureStream);
        return picture;
    }
    return _missingImage;
}
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);           
    });
}