Страницы

Поиск по вопросам

четверг, 14 февраля 2019 г.

Работа с Backgroundworker и Dispatcher

Пишу программу на C# с mvvm. У меня есть два эквивалентных куска кода, в которых,по-моему мнению, должна происходить абсолютно одинаковая работа. Суть в чем- постепенная подгрузка(добавление) элементов в коллекцию с помощью BackgroundWorker. Коллекция имеет биндинг с listview и соответственно постепенное(пообъектное) добавление в коллекцию отображается в этом listview. Код:
public class ListContactViewModel : ViewModelBase { public ObservableCollection DialogListFirstPage { get;set;} Dispatcher _dispatcher; public ListContactViewModel(VkApi vk) { _dispatcher = Application.Current.Dispatcher;
DialogListFirstPage = new ObservableCollection(); var bw1 = new BackgroundWorker(); bw1.DoWork += (o, e) => { FirstDialogPageMethod(vk); //Создание коллекции диалогов
}; bw1.RunWorkerAsync();
//внимания в этом методе достойна только строчка добавления в коллекцию private void FirstDialogPageMethod(VkApi vk) { int totalCount, unreadCount; var GetFirstDialigPage = vk.Messages.GetDialogs(20, 0, out totalCount, out unreadCount);
foreach (var i in GetFirstDialigPage) {
var Names = vk.Users.Get(i.UserId.ToString(), ProfileFields.FirstName);
_dispatcher.Invoke(()=> DialogListFirstPage.Add(new MessageChild() { AuthorFirstName = Names.FirstName, AuthorLastName = Names.LastName, Body = i.Body, UserId = i.UserId, Date = i.Date, ChatActiveIds = i.ChatActiveIds, Title = i.Title, UsersCount = i.UsersCount, ChatId = i.ChatId }));
} }
на всякий случай приведу строчку биндинга из xaml:

И все закономерно: окно открывается пустым и я наблюдаю постоянное добавление элементов в список.
Ситуация 2: Из предыдущего окна я перехожу в следующее , в котором аналогичная ситуация
public class CurrentDialogViewModel:ViewModelBase { public ObservableCollection ReadyCollection { get; set; } Dispatcher disp; public CurrentDialogViewModel(VkApi vk,MessageChild parametr) { disp = Application.Current.Dispatcher; ReadyCollection = new ObservableCollection(); var bw2 = new BackgroundWorker(); bw2.DoWork += (p, m) => {
MoreMessages();
}; bw2.RunWorkerAsync(); private void MoreMessages() { foreach (var i in builder.ConcreateDialogCreater(ids)) { disp.Invoke(() => ReadyCollection.Insert(0, i));
} Datas.offset += 200;
}
где builder.ConcreateDialogCreater(ids) возвращает
ObservableCollection
Xaml:

Так вот в этом случае при открытии окна, оно у меня некоторое время остается пустым, после чего список мгновенно отображает все объекты в ReadyCollection. От Insert это не зависит, с Add тоже самое. Также это не зависит от builder.ConcreateDialogCreater(ids), потому что пробовал делать просто инициализацию объекта при добавлении в цикле
ReadyCollection.Add(new MessageChild());
Аналогичная история-объекты вываливаются всем скопом по окончании добавления последнего. А я хочу добиться постепенной подгрузки, как в предыдущем окне. Почему так происходит и что нужно исправить?
UPD: Продебажил еще раз - все таки я был не прав и задержка связана с выполнением метода builder.ConcreateDialogCreater(ids). И пока он не выполнится весь- foreach не начнется. Ведь в первом случае я коллекцию заполняю непосредственно в том классе и задержка обоснована работой библиотечных методов перед добавлением. Во-втором же случае нужно ждать,пока метод выполнится полностью.


Ответ

В DoWork вместо Dispatcher используйте ReportProgress. (для его работы надо включить WorkerReportsProgress).
partial class MainWindow : Window { public MainWindow() { this.DataContext = _List = new ObservableCollection(); }
ObservableCollection _List;
private void Button_Click(object sender, RoutedEventArgs e) { var w = new BackgroundWorker() { WorkerReportsProgress = true }; w.DoWork += (s, we) => { for (var i = 0; i < 50; i++) { Thread.Sleep(100); // тут что-то делаем w.ReportProgress(0, i); } }; w.ProgressChanged += (s, we) => // выполняется в основном потоке _List.Add(new Message() { Text = "t" + we.UserState }); w.RunWorkerAsync(); }
class Message { public string Text { get; set; } } }

Комментариев нет:

Отправить комментарий