Создание пользовательского интерфейса WPF на рабочем месте? - PullRequest
3 голосов
/ 12 января 2009

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

  • Вызов конструктора
    Органы управления?
  • Задать свойства элементов управления?
  • Настройка привязки данных?
  • Создание логического дерева (добавление потомков)?

Какие еще есть варианты для повышения производительности при создании интерфейса? Хорошая идея - приостановить работу диспетчера или вызвать FrameworkElement.BeginInit?

Ответы [ 3 ]

4 голосов
/ 12 января 2009

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

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

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

1 голос
/ 12 января 2009

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

Если бы это был случай, когда долгое время было связано со сбором и упорядочением данных, тогда лучшим вариантом был бы фоновый поток, а затем вызывать любые свойства зависимостей пользовательского интерфейса, используя объект 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); }));
}
0 голосов
/ 02 июня 2009

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...