在执行我的应用程序时,我需要知道在决定是拍摄实时照片还是“不可用照片”之前是否有照片(来自指定的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
}
非常感谢!
答案 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不会冻结)。
这是你在找什么?