2010-03-24 7 views
15

У меня есть элемент управления, который имеет свои данные, привязанные к стандарту ObservableCollection, и у меня есть фоновая задача, которая вызывает службу для получения большего количества данных.Обновление привязки WPF в фоновом потоке

Я хочу, чтобы обновить мои данные под моим контролем, показывая диалог «Подождите», но когда я добавляю новые элементы в коллекцию, поток пользовательского интерфейса блокируется, когда он повторно связывается и обновляется мой контроль.

Могу ли я обойти это, чтобы мои анимации и прочее продолжали работать в диалоговом окне «Подождите»?

Или, по крайней мере, дать «внешний вид» пользователю, что его не заперли?

ответ

17

Если я правильно понимаю, вы уже используете BackgroundWorker для извлечения данных, а просто назначая эти данные к ObservableCollection является запиранием интерфейса ,

Один из способов избежать блокировки пользовательского интерфейса - назначить данные ObservableCollection в небольших кусках путем очередности нескольких методов диспетчера. Между каждым вызовом метода можно обрабатывать события пользовательского интерфейса.

следующее добавило бы один элемент одновременно, это немного экстремально, но это иллюстрирует концепцию.

void UpdateItems() 
{ 
    //retrievedItems is the data you received from the service 
    foreach(object item in retrievedItems) 
     Dispatcher.BeginInvoke(DispatcherPriority.Background, new ParameterizedThreadStart(AddItem), item);  
} 

void AddItem(object item) 
{ 
    observableCollection.Add(item); 
} 
+0

Итак, добавив их в коллекцию элементов в фоновом потоке, обновите элементы управления в потоке пользовательского интерфейса? Как это работает? – Mark

+3

Нет, вы добавляете их в поток диспетчера, но с более низким приоритетом и в меньших пучках, поэтому пользовательский интерфейс остается отзывчивым. – Bubblewrap

+0

Я вижу, спасибо, я попробую – Mark

0

использовать BackgroundWorker для выполнения этой задачи. обновить obsrvablecollection в методе DoWork

+0

Как это обновление пользовательского интерфейса поток? Может быть, я не понимаю этого, но arent все мои элементы управления и рендеринг позади них в потоке пользовательского интерфейса, поэтому обновление коллекции в фоновом потоке приведет к тому, что поток пользовательского интерфейса будет выполнять нормальное обновление? – Mark

-1

Используйте это:

 

Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Render, new Action(UpdateData), value); 

private void UpdateData(int value) 
{ 
    BindingSourceProperty = value; 
} 

 
+1

что делать? Почему это хорошо? Как это мне поможет? – Mark

+0

позволяет утверждать, что у вас есть форма интерфейса WPF с элементом управления ProgressBar как , где PercentValue является свойством, определенным в вашем DataContext, прикрепленном к форме. Предполагая сценарий, в котором вы получаете данные обратного вызова по некоторому другому потоку, указывающие ход работы, и вы хотите отображать его в пользовательском интерфейсе. Если вы установите это значение непосредственно в свойстве источника зависимостей «PercentValue», вы получите обновление ui только при завершении операции фонового потока. Итак, чтобы получить процентное значение в реальном времени, установите это значение как указано выше – RockWorld

10

ObservableCollection поднимет CollectionChanged события, которые заставят интерфейс пересвязать данных, меры, организовать и перерисовки. Это может занять много времени, если у вас появилось много обновлений.

Можно заставить пользователя думать, что пользовательский интерфейс жив, разделив работу в небольших пакетах. Используйте диспетчер из потока пользовательского интерфейса (любой элемент управления имеет ссылку на него), чтобы запланировать действия по обновлению коллекций с 10-100 элементами (определите число путем эксперимента, это просто для поддержки идеи).

Ваш фон код может выглядеть следующим образом:

void WorkInBackground() 
{ 
    var results = new List<object>(); 

    //get results... 

    // feed UI in packages no more than 100 items 
    while (results.Count > 0) 
    { 
     Application.Current.MainWindow.Dispatcher.BeginInvoke(
      new Action<List<object>>(FeedUI), 
      DispatcherPriority.Background, 
      results.GetRange(0, Math.Min(results.Count, 100))); 
     results.RemoveRange(0, Math.Min(results.Count, 100)); 
    } 
} 
void FeedUI(List<object> items) 
{ 
    // items.Count must be small enough to keep UI looks alive 
    foreach (var item in items) 
    { 
     MyCollection.Add(item); 
    } 
} 
0

У меня есть DLL, которая запускает рабочий поток и отправляет события обратно в приложение - отлично работает на окна формы, переключился на WPF и все перестало работать. Я бил головой о кирпичную стену в течение 4 часов, пытаясь заставить ее работать. Но решение, с которым я столкнулся, благодаря Microsoft UI Thread Safe marshalling EnableCollectionSynchronization, дает действительно чистую реализацию для решения этой проблемы.

Эта коллекция расширяет ObservableCollection и реализует EnableCollectionSynchronization, позволяющую использовать эти объекты между WPF, а также фоновыми работниками.

Редактировать: Microsoft's docs сказать следующее, так что я буду считать, что совместное использование объект контекста не имеет значения.

Параметр контекста - это произвольный объект, который можно использовать для информации, известной при включении синхронизации синхронизации. Контекст может быть null.

ThreadSafeCollection.cs

using System.Collections.ObjectModel; 
using System.Windows.Data; 

namespace NSYourApplication 
{ 
    /// <summary> 
    /// This ObservableCollection is thread safe 
    /// You can update it from any thread and the changes will be safely 
    /// marshalled to the UI Thread WPF bindings 
    /// Thanks Microsoft! 
    /// </summary> 
    /// <typeparam name="T">Whatever type of collection you want!</typeparam> 
    public class ThreadSafeCollection<T> : ObservableCollection<T> 
    { 
     private static object __threadsafelock = new object(); 

     public ThreadSafeCollection() 
     { 
      BindingOperations.EnableCollectionSynchronization(this, __threadsafelock); 
     } 
    } 
} 

продукта примера WindowViewModel WindowViewModel.cs

namespace NSYourApplication 
{ 
    /// <summary> 
    /// Example View 
    /// BaseModelView implements "PropertyChanged" to update WPF automagically 
    /// </summary> 
    class TestViewModel : BaseModelView 
    { 
     public ThreadSafeCollection<string> StringCollection { get; set; } 

     /// <summary> 
     /// background thread implemented elsewhere... 
     /// but it calls this method eventually ;) 
     /// Depending on the complexity you might want to implement 
     /// [MethodImpl(MethodImplOptions.Synchronized)] 
     /// to Synchronize multiple threads to prevent chase-conditions,deadlocks etc 
     /// </summary> 
     public void NonUIThreadMethod() 
     { 
      // No dispatchers or invokes required here! 
      StringCollection.Add("Some Text from a background worker"); 
     } 

     /// <summary> 
     /// Somewhere in the UIThread code it'll call this method 
     /// </summary> 
     public void UIThreadMethod() 
     { 
      StringCollection.Add("This text come from UI Thread"); 
     } 

     /// <summary> 
     /// Constructor, creates a thread-safe collection 
     /// </summary> 
     public TestViewModel() 
     { 
      StringCollection = new ThreadSafeCollection<string>(); 
     } 
    } 
} 

использования в ListBox в XAML окне/управления MainWindow.xaml

<ListBox x:Name="wpfStringCollection" ItemsSource="{Binding StringCollection,Mode=OneWay}"> 

    </ListBox> 
+0

Хм, думая об этом; этот единственный статический __threadsafelock будет использоваться всеми коллекциями; это может вызвать проблемы;) Мне придется вернуться к этому ... =] –

Смежные вопросы