#c# #system.reactive #reactiveui
#c# #system.reactive #reactiveui
Вопрос:
Мне нужно обработать большой набор данных в фоновом потоке, а затем отобразить эти данные в элементах управления DataGrid в интерфейсе WPF. Данные поступают с внешнего промышленного устройства, для которого мне предоставлена сторонняя библиотека. Итак, я получаю данные в делегате из этой библиотеки.
Моя проблема в том, что я, похоже, не могу запланировать операцию в потоке MainUI из фонового потока, и я не уверен, как решить эту проблему.
Модель представления данных:
public class DataViewModel : ReactiveObject
{
public SourceList<SummaryData> SummaryData = new SourceList<SummaryData>();
public ReadOnlyObservableCollection<SummaryData> SummaryDataView;
public SourceList<EnergyTable> SampleData = new SourceList<EnergyTable>();
public ReadOnlyObservableCollection<EnergyTable> SampleDataView;
public DataViewModel()
{
SummaryData.Connect()
.Bind(out SummaryDataView)
.Subscribe();
SampleData.Connect()
.Bind(out SampleDataView)
.Subscribe();
}
}
Делегат сторонней библиотеки отправляет данные.
protected void OnSqlCommandCode(Tag tag)
{
// Process the
Observable.Start(
() => HandleData(),
RxApp.TaskpoolScheduler)
.ObserveOn(RxApp.MainThreadScheduler);
}
Данные обрабатываются…
public void HandleData()
{
ImpactorResultData resu<
... Processing ...
// NOW I TRY TO UPDATE THE COLLECTIONS FROM THE MAIN UI THREAD
Observable.Start(
() => UpdateUiThreadCollections(result),
RxApp.MainThreadScheduler
);
}
Я обновляю экземпляр DataViewModel. Я ожидаю, что, поскольку я запланировал выполнение операции в потоке пользовательского интерфейса (выше), все пройдет хорошо. Вместо этого я получаю исключение, что коллекции, привязанные к DataGrid, должны обновляться в основном потоке пользовательского интерфейса.
/// <summary>
/// Collections tied to data linked to a UI control need to be called from the
/// UI thread.
///
/// The other option is to remake the ObservableRangeCollection every time
/// from the list.
/// </summary>
protected static void UpdateUiThreadCollections(
ImpactorResultData result
)
{
dataViewModel.SummaryData.Add(result.SummaryData);
dataViewModel.SampleData.Edit(innerList =>
{
innerList.Clear();
innerList.AddRange(result.Samples);
});
}
По какой-то причине оба:
Observable.Start(
() => UpdateUiThreadCollections(result),
RxApp.MainThreadScheduler
);
RxApp.MainThreadScheduler.Schedule(() =>
{
UpdateUiThreadCollections(result);
});
все еще планируйте операцию в фоновом потоке. Должно быть что-то, чего я не понимаю в ReactiveUI или реактивных расширениях.
Заранее благодарю вас за любую информацию.
Комментарии:
1. С вызовами, подобными
Observable.Start(() => UpdateUiThreadCollections(result), RxApp.MainThreadScheduler)
наблюдаемому, фактически не запускается, пока вы не вызовете.Subscribe
. Как вы подписываетесь на эти наблюдаемые?2.
Observable.Start( () => UpdateUiThreadCollections(result), RxApp.MainThreadScheduler);
функция вызывается без сбоев, нет необходимости подписываться.
Ответ №1:
Вы должны иметь возможность обновлять свой SourceList
из фонового потока. Вам не нужна вся эта сложная логика. Вместо этого используйте System.Reactive.Linq
и ObserveOn()
расширение непосредственно перед выполнением привязки к свойству, которое фактически использует ваш пользовательский интерфейс.
В вашей viewmodel:
SummaryData.Connect()
.ObserveOn(RxApp.MainThreadScheduler)
.Bind(out SummaryDataView)
.Subscribe();
SampleData.Connect()
.ObserveOn(RxApp.MainThreadScheduler)
.Bind(out SampleDataView)
.Subscribe();
О, и я не пользователь Wpf, но я помню, что были некоторые дополнительные пакеты ReactiveUI специально для Wpf.
Комментарии:
1. Я не уверен, правильно ли это. Подробнее см. Мой комментарий к вопросу.
2. Это был мой оригинальный подход, и он не сработал. Он все еще пытался обновить коллекцию из фонового потока.
3. Просто чтобы убедиться, что вы установили ReactiveUI. Пакет Wpf в дополнение к обычному пакету ReactiveUI? Потому что пакет Wpf переопределяет свойство RxApp.MainThreadScheduler на что-то отличное от значения по умолчанию.
4. Это правильный ответ и стандартный подход для сортировки обновлений в потоке пользовательского интерфейса.
5. Lee — Существует основной проект, который представляет собой проект .NET Framework / WPF. Однако модель данных, запускающая событие, находится в другом проекте, который является .NET Standard 2.0. Означает ли это, что я создал два MainThreadSchedulers?