Построение пользовательского интерфейса WPF на рабочем потоке?



Мне нужно создать довольно сложный WPF UI (сетка Хьюга) в коде (без XAML invloved). Есть ли способ остановить блокировку основного потока пользовательского интерфейса в процессе построения сетки? Есть ли какие-то части построения пользовательского интерфейса, которые можно передать на аутсорсинг рабочему потоку? Какие части создания пользовательского интерфейса на самом деле должны быть в потоке пользовательского интерфейса?




  • вызов конструктора

    Контроль?

  • Настройка свойств элементов управления?

  • Настройка Привязка данных?

  • построение логического дерева (добавление потомков)?


Какие еще параметры я могу использовать для повышения производительности при создании пользовательского интерфейса? Приостанавливает работу диспетчера или вызывает FrameworkElement.Начать с хорошей идеи?

720   3  

3 ответов:

Если вы можете разбить построение пользовательского интерфейса на шаги, вы можете выполнить каждый из этих шагов в виде отдельного сообщения. Это позволит обрабатывать другие сообщения между ними. Что-то вроде:

private delegate void BuildHandler();

private void BuildGridPart1()
{
    //build first part of grid

    Dispatcher.BeginInvoke(new BuildHandler(BuildGridPart2), DispatcherPriority.Background);
}

private void BuildGridPart2()
{
    //build second part of grid

    Dispatcher.BeginInvoke(new BuildHandler(BuildGridPart3), DispatcherPriority.Background);
}

//etc

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

Мне кажется, что у вас может быть не так много вариантов, кроме запуска в потоке пользовательского интерфейса.

Если бы это был случай, когда длительное время было связано со сбором и упорядочиванием данных, то фоновый поток был бы лучшим вариантом, а затем вызывал бы любые свойства зависимостей пользовательского интерфейса, используя объект Dispatcher. Я предполагаю, что время не для сбора каких-либо данных, а исключительно для программного создания элементов UIElements.

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

Затем все свойства зависимостей вызывают Dispatcher.VerifyAccess при записи, что вызовет исключение, если доступ осуществляется в неправильном потоке. Таким образом, это исключает обновление привязки данных и свойств в другом потоке.

Моя первая реакция будет такой, как написал @Kent.
Чтобы немного уточнить, если у вас есть цикл foreach для каждой строки с другим циклом foreach для каждого столбца, то вы можете даже хотя вы находитесь в правильном потоке пользовательского интерфейса, вызовите Dispatcher.BeginInvoke, чтобы" уступить " на мгновение от вашего долго работающего метода. то есть разбить его, а затем передать управление обратно в поток пользовательского интерфейса, чтобы позже продолжить добавление в сетку. Усилия по компоновке могут быть значительно больше, и общее время будет больше, но немного более отзывчивым.

private void Window_Loaded(object sender, RoutedEventArgs e)
{
  CreateRow(1);
}

private void CreateRow(int i)
{
  // 
  // Construct row i of the grid
  //
  Dispatcher.BeginInvoke(DispatcherPriority.Background, new EventHandler(delegate { CreateRow(i + 1); }));
}

Есть ли причина, по которой вы должны построить его в коде? Если это связано с тем, что сетка динамическая (т. е. столбцы не известны до времени выполнения), есть способы обойти ее.

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

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

Если вы используете хорошую сетку, она, вероятно, использует виртуализацию и имеет API для подключения к событиям виртуализации. (По сути, сетка строит пользовательский интерфейс только так, как ему это нужно, или запрашивает элементы пользовательского интерфейса, как ему это нужно). Этот позволяет избежать построения всей сетки при нагрузке.

Comments

    Ничего не найдено.