Реактивные расширения, обновляющие пользовательский интерфейс

Я делаю этот асинхронный веб-запрос несколько раз (может быть дважды, трижды или даже 6 раз в зависимости от условий)

        var request = HttpWebRequest.CreateHttp(url);

        var observableRequest = Observable.FromAsyncPattern<WebResponse>(
            request.BeginGetResponse, request.EndGetResponse);

        Observable.Timeout(observableRequest.Invoke(), TimeSpan.FromSeconds(120)).
            Subscribe(response => { HandleListResult(response); },
            exception => { HandleListResultTimeOut(exception); });

У меня есть коллекция (список) в ViewModel, которая имеет привязку к LisBox, и я хотел бы продолжать добавлять в коллекцию после возврата каждого ответа.

Как лучше всего это сделать с помощью Reactive Extensions? Было бы здорово, если бы кто-нибудь показал мне пример кода!

заранее спасибо


person user636525    schedule 27.02.2011    source источник
comment
Поскольку вы будете обновлять пользовательский интерфейс либо с помощью свойства, вызывающего PropertyChanged, либо с помощью ObservableCollection, лучше всего использовать ObserveOnDispatcher.   -  person Maciek    schedule 27.02.2011


Ответы (2)


Вы можете преобразовать поток URL-адресов непосредственно в потоки:

    public static IObservable<Stream> RequestToStream(
        this IObservable<string> source, TimeSpan timeout)
    {
        return
            from wc in source.Select(WebRequest.Create)
            from s in Observable
                .FromAsyncPattern<WebResponse>(wc.BeginGetResponse,
                    wc.EndGetResponse)()
                .Timeout(timeout, Observable.Empty<WebResponse>())
                .Catch(Observable.Empty<WebResponse>())
            select s.GetResponseStream();
    }

И затем вам нужно наблюдать за своими ответами в пользовательском интерфейсе, вам нужно использовать .ObserveOnDispatcher(), например:

        Observable
            .Return("www.msdn.com")
            .RequestToStream(TimeSpan.FromSeconds(1))
            .ObserveOnDispatcher()
            .Subscribe(request => UpdateUI(Request));
person Sergey Aldoukhov    schedule 27.02.2011
comment
Спасибо ! Нужно ли мне блокировать объект List‹string› (который привязан к ListBox) перед его обновлением? Поскольку один и тот же обработчик будет вызываться несколько раз, и мне интересно, что произойдет, когда второй обработчик попытается обновить список, когда первый обработчик уже обновляет его. Спасибо ! - person user636525; 28.02.2011
comment
Не может быть. Каждый запрос на обновление пользовательского интерфейса будет выполняться в одном и том же потоке пользовательского интерфейса, поэтому они будут сериализованы. Нет необходимости запирать. - person Sergey Aldoukhov; 28.02.2011
comment
Вы также можете использовать «Отложить» и «Повторить попытку», чтобы повысить надежность вызова (т.е. если один из них истечет, попробуйте еще раз), но здесь великолепное использование Rx! - person Ana Betts; 03.06.2011

В ReactiveUI это делается с помощью CreateCollection().

IObservable<string> source; // Maybe this is a Subject<string> or whatever

myBoundCollection = source
    .SelectMany(webServiceCall) // This is your FromAsyncPattern func
    .CreateCollection();  // Pipe the Observable to a Collection

ReactiveUI обрабатывает все материалы ObserveOn, чтобы убедиться, что они синхронизированы, находятся в правильных потоках и т. д. Этот вызов немедленно возвращается с пустым списком, а затем по мере поступления результатов коллекция заполняется.

person Ana Betts    schedule 02.03.2011