Синхронно получаем картинку (из Ури) - PullRequest
0 голосов
/ 13 февраля 2012

При выполнении моего приложения мне нужно знать, доступно ли изображение (с указанного URI), прежде чем я решу, сделать ли мне живое изображение или "изображение недоступно".

Поскольку изображение устанавливается только один раз, мне нужно синхронно знать, доступно ли изображение и не повреждено ли оно.Я пытался использовать Reactive Extensions, но мне так и не удалось получить первый элемент (целое приложение зависает при выполнении оператора .First ())

Следующий blogger осветил мою проблемуно его код даже не компилировался.Я думаю, это может быть связано с новым использованием синтаксиса Reactive.Кроме того, Reactive все еще находится в бета-версии с Silverlight 5, и я думаю, что он все еще может вести себя необычно.

Я открыт для любого другого возможного решения, но лучшим решением для меня будет возможность создать методы расширения OpenRead () для класса Webclient и использовать его следующим образом:

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

Большое спасибо!

Ответы [ 3 ]

0 голосов
/ 13 февраля 2012

Rx просто отлично подходит для такого типа вещей.Я считаю, что вопрос не так, вы действительно хотите быть Async.Вы просто хотите продолжить результат веб-запроса.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")
    );

Затем вам просто нужно убедиться, что вы подписались на другой поток (например, используя потокпул) и убедитесь, что вы обновляете в потоке пользовательского интерфейса.Мы уже должны использовать 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).

Также у меня есть серия блогов, которые могут помочь вам лучше понять Rx (например, не используйте .First ())

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

0 голосов
/ 14 февраля 2012

Версия Silverlight (пришлось установить ее на новом ПК).

Предполагая, что вы определили постоянный или «статический только для чтения» экземпляр отсутствующего изображения, которое нужно показать, это ваш бизнес-логик

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

Тогда у вас может быть способ получить изображение с помощью Rx, но с помощью веб-клиента SL под капотом.

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 выполняет все потоки за вас, поэтому не требуется никаких расписаний, и все остается приятным и отзывчивым (т. Е. Пользовательский интерфейс не останавливается).ищете?

0 голосов
/ 13 февраля 2012

Ошибка, которую некоторые совершают, состоит в том, что они могут оставить неизменным свой синхронный код (в вашем случае код, который ожидает, что сможет вызвать функцию, которая возвращает изображение) и каким-то образом просто сделать его частьюсправиться с тем, чтобы быть асинхронным.Это просто невозможно, поскольку функция, вызываемая вашим кодом, должна стать асинхронной, как и сам вызывающий код, так же как и код, вызывающий этот код, и т. Д. Вплоть до любого события, инициировавшего код.

Новые возможности языка C # на горизонте призваны облегчить эту ситуацию, но в данный момент они вам недоступны.Все равно потребуется весь код в цепочке, чтобы понять, что он асинхронный.

Я часто вижу, что Reactive рекламируется как решение такого рода проблемы, однако Reactive на самом деле не предназначен для решения этой проблемы, и выв конечном итоге прыгаю через некоторые уродливые обручи, пытаясь заставить его работать.

Поэтому я предлагаю решение, которое я использую в качестве блога здесь .С этим кодом (и он требует удивительно короткого количества кода, добавленного в проект) ваш код может быть записан как: -

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