WPF Multithreading: используя Dispatcher, но пользовательский интерфейс все еще зависает? - PullRequest
2 голосов
/ 10 ноября 2010

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

Прямо сейчас у меня есть главное окно, создающее View и ViewModel. Затем внутри нового потока (с помощью Dispatcher) он устанавливает View.DataContext = ViewModel. Очень большая коллекция ObservableCollection создается лениво, когда связывание запускает замедление. Однако, похоже, что некоторые другие элементы пользовательского интерфейса, которые должны появиться до этого замедления, на самом деле не отображаются.

   private void ButtonClick(Object sender, RoutedEventArgs e)
   {
        MyView view = new MyView();
        MyViewModel vm = new MyViewModel();

        TabItem tabItem = new TabItem();
        tabItem.Header = "MyView";
        tabItem.Content = view;

        MyTabCollection.Items.Add(tabItem);

        Window working = new Working();
        working.Show();

        ThreadStart thread = delegate()
        {
            DispatcherOperation operation = Dispatcher.BeginInvoke(
                DispatcherPriority.Normal,
                new Action(delegate()
                {
                    view.DataContext = vm;
                    ((FrameworkElement)view.Parent).Focus();
                    working.Close();
                }
                )
            );
        };

        Thread theThread = new Thread(thread);
        theThread.Start();
    }

В основном это говорит о том, что он должен создать представление и модель представления, а затем добавить представление в имеющуюся у меня коллекцию вкладок (что означает, что он должен показывать новую вкладку как минимум). И это также должно показать окно «Работает ...». После этого отдельный поток должен связать ViewModel с представлением, сфокусироваться на этой вкладке и закрыть рабочее окно. Проблема в том, что первая часть не отображается, пока все не будет сделано; Вкладка не отображается, и рабочее окно не отображается до тех пор, пока фактически не завершится новый поток (что приводит к немедленному отображению / закрытию рабочего окна). Я предполагаю, что это может быть связано с тем, как я получаю данные, но я не уверен. Вот как это происходит:

  1. Создать вид
  2. Создать ViewModel
  3. Создайте TabItem с Content, установленным в View, и добавьте TabItem в TabCollection.
  4. Создать / показать окно «Working ...»
  5. Диспетчер: установите View.DataContext = ViewModel. Это событие вызывает привязку DataBindings, которая в свою очередь захватывает ObservableCollection. Так как OC создается Lazily, он сейчас создается (это узкое место). <- Это портит мой отдельный поток / диспетчер? </li>
  6. Диспетчер: установить фокус на вкладку
  7. Закрыть окно «Working ...»

Ответы [ 3 ]

5 голосов
/ 10 ноября 2010

Все, что делает ваш дополнительный поток, - это отправка еще одного вызова потоку диспетчера.Предположительно, вы действительно хотите выполнить работу в дополнительном потоке, или нет никакого смысла в его создании.

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

2 голосов
/ 11 ноября 2010

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

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

Для коллекций: вы можете определить специальные коллекции, которые возвращают только элементы, которые уже были загружены из вышестоящего источника данных, а затем инициировать загрузку дополнительных элементов в отдельном потоке, когда кто-то подписывается на INotifyCollectionChanged. Когда дополнительные элементы поступят, инициируйте события INotifyCollectionChanged. Если INotifyCollectionChanged отписан, отмените все ожидающие загрузки.

Для итогов и тому подобного: та же идея. По мере того, как данные поступают, общее увеличение и события происходят (автоматически для DependencyProperty или с использованием INotifyPropertyChanged).

Кроме того, уровень данных должен иметь параллельное свойство для каждой коллекции, суммы или другого загруженного с задержкой значения, указывающего, полностью ли оно загружено или нет, что позволяет пользовательскому интерфейсу выделять серые участки, которые не загружены полностью. Также удобно иметь общий флаг «загрузки», который можно использовать для выделения серого цвета в разделах пользовательского интерфейса, когда загружается что-либо вообще (так проще написать пользовательский интерфейс).

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

0 голосов
/ 18 ноября 2010

Ваш DispatcherPriority установлен на Normal - попробуйте установить его на Background, так как это может улучшить рендеринг

...