2017-01-19 8 views
2

У меня есть приложение WPF, которое запустит долговременную задачу (60 секунд), которая использует System.Reactive.Subject<string> для изменения статуса сообщений. Идея заключалась в том, что я мог бы затем Подписываться на наблюдаемый из моего ViewModel и иметь ReactiveUI автоматически обновлять свой интерфейс через привязку данных. Все это прекрасно работает, за исключением того, что TextBox не обновляется в режиме реального времени. Он обновляется только после завершения долговременной задачи. Я предполагаю, что это связано с тем, что мой поток пользовательского интерфейса блокируется и не может обновляться.Использование ReactiveUI, Observables, SubscribeOn и ObserveOn для отображения журнала вывода в пользовательском интерфейсе во время долговременного процесса

Работая в соответствии с этим предположением, в моем исследовании было предложено разместить подписку на фоновом потоке с использованием SubscribeOn, а затем направить уведомления обратно на поток пользовательского интерфейса, используя ObserveOnDispatcher. Тем не менее, это все еще не привело к результатам, которые я хотел - пользовательский интерфейс обновлялся только после долговременной задачи.

Может ли кто-нибудь дать мне некоторое представление о том, что мне нужно сделать, чтобы мой журнал регистрации обновлялся в реальном времени? Ниже приведены соответствующие фрагменты кода.

XAML:

<TextBox Grid.Row="1" Text="{Binding Output}" IsReadOnly="True" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" Margin="10,0,10,10" x:Name="OutputTextBox" /> 

Code-Behind:

protected override void OnContentRendered(EventArgs e) 
     { 
      if (Converter == null) return; 
      _viewModel = new ConversionOutputWindowViewModel(Converter); 
      DataContext = _viewModel; 
      _viewModel.StartConversion(); // Long-running Task 
      //_viewModel.StartSave(); 
      FinishButton.IsEnabled = true; 
     } 

ViewModel:

private string _output; 

public string Output // Data bound in XAML 
     { 
      get { return _output; } 
      set { this.RaiseAndSetIfChanged(ref _output, value); } 
     } 

public void StartConversion() 
     { 
      _edmxConverter.Convert(); // Long-running Task 
     } 

public ConversionOutputWindowViewModel(Utilities.Converters.EdmxConverter converter) 
     { 
      _edmxConverter = converter; 
      _compositeDisposable.Add(_edmxConverter.Output 
                .SubscribeOn(NewThreadScheduler.Default) 
                .ObserveOnDispatcher() 
                .Subscribe(s => Output = Output += s)); 
      //_compositeDisposable.Add(_edmxConverter.Output.Subscribe(s => Output = Output += s)); 
     } 

долгоиграющих Задача Функция:

public Subject<string> Output { get; } 

Output = new Subject<string>(); //In ctor 

private void PrintReplacement(XAttribute attribute, string oldValue, string newValue, int level, Verbosity minVerbosity = Verbosity.Informational) 
     { 
      if (Verbosity < minVerbosity) return; 
      Output.OnNext($"{new string('\t', level)}{attribute.Name}: {oldValue} -> {newValue}{Environment.NewLine}"); 
     } 

Возможно, это поможет обернуть мой вызов функции Long-running Task внутри await Task.Run? Я хватаюсь за соломинку здесь. У меня нет хороших рабочих знаний о потоке .NET.

ответ

0

Ну, вы используете предметы, поэтому .SubscribeOn(NewThreadScheduler.Default) ничего не делает. (Стандартное старое барабанное избиение) Не используйте предметы.

Ваш длительный процесс должен быть вызовом метода, который возвращает IObservable<T> (вместо того, чтобы иметь класс, который предоставляет свойство). T должны быть обновлениями статуса, которые вы хотите получить. Когда задача будет выполнена, вы будете OnComplete. Если задача не выполнена, вы используете OnError.

В идеале метод, возвращающий IObservable с использованием Observable.Create, чтобы выполнить работу, которая должна быть выполнена.

public IObservable<string> Convert() 
{ 
    return Observable.Create<string>(observer=> 
    { 
     //do stuff here. 
     //Call observer.OnNext with status messages 
     //When done call observer.OnCompleted() 
    }); 
} 

Поскольку вы не показываете, какова ваша долгосрочная задача, я больше не могу помочь с реализацией.

+0

Спасибо за ответ. Это очень интересно и, вероятно, действительно служит для отображения моего очень рудиментарного понимания Reactive. Не могли бы вы объяснить, почему использование предметов плохо? Мое понимание заключалось в том, что «Subject » был просто объектом, который предоставлял стандартные реализации как «IObservable», так и «IObserver». Я хотел использовать тему, потому что у меня есть несколько методов «Печать», которые все вызывают «Output.OnNext (s)». Я не понимал, что это важно, иначе я бы включил его в вопрос. Я не уверен, как этот шаблон будет работать при использовании «Observable.Create» –

+0

https://leecampbell.com/2016/10/03/iprogress-and-rx-without-subjects/ –