Страницы

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

понедельник, 6 января 2020 г.

Работа с Backgroundworker и Dispatcher

#c_sharp #многопоточность #mvvm #backgroundworker


Пишу программу на 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 не начнется.
Ведь в первом случае я коллекцию заполняю непосредственно в том классе и задержка
обоснована работой библиотечных методов перед добавлением.
Во-втором же случае нужно ждать,пока метод выполнится  полностью.
    


Ответы

Ответ 1



В 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; } } }

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

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