Повышение производительности WPF путем разделения пользовательского интерфейса на «регионы» — возможно ли это?

#c# #wpf #performance

#c# #wpf #Производительность

Вопрос:

Я провел очень простой тест производительности в клиентском приложении WPF:

 public partial class MainWindow : Window
{
    private ObservableCollection<int> data = new ObservableCollection<int>();
    public ObservableCollection<int> DataObj { get { return data; } }

    private void button1_Click(object sender, RoutedEventArgs e)
    {
        for (int j = 0; j < 5; j  )
        {
            Thread t = new Thread(() =>
                {
                    for (int i = 0; i < 100; i  )
                    {
                        Thread.Sleep(5);
                        Dispatcher.Invoke(new Action(() => { data.Add(1); })); //updates the count
                        Dispatcher.Invoke(new Action(() => { richTextBox1.AppendText("1"); })); //updates the string data
                    }
                });

            t.Start();
        }
    } 
  

Затем у меня есть два элемента управления в пользовательском интерфейсе: a TextBlock и a RichTextBox .

TextBlock Привязывается к Count свойству источника данных, в то время как RichTextBox добавляет каждое новое значение данных к своей текстовой строке (т. е. отображает содержимое данных).

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

Есть ли способ разорвать эту зависимость от производительности? Я понимаю, что RichTextBox вполне может быть медленным, но почему он должен замедлять работу текстового блока, который в противном случае быстро сокращается?

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

1. Привет, тестовый пример производительности. Похоже, что это еще не вымерший вид.

2. Что произойдет, если отключить привязку TextBlock?

3. RichTextBox по-прежнему обновляется со скоростью около 2 кадров в секунду. Я думаю, мне нужно несколько потоков диспетчера, но я не нашел никакого способа создать единый пользовательский интерфейс из нескольких панелей / окон.

4. Я знаю обходной путь: Dispatcher.Invoke(new Action(() => { richTextBox1.AppendText("1"); }), DispatcherPriority.Background); . Я не совсем понял проблему, но этот код выполняется быстрее, чем вызов с обычным приоритетом.

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

Ответ №1:

Специфика WPF заключается в том, что на каждое окно приходится только один поток пользовательского интерфейса.

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

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

  1. SystemIdle
  2. ApplicationIdle
  3. ContextIdle
  4. Предыстория
  5. Ввод
  6. Загружено
  7. Визуализация
  8. Привязка данных
  9. Нормальный
  10. Отправить

Значение по умолчанию равно Normal (9) , это почти наивысший приоритет, и оно неявно применяется всякий раз, когда вы вызываете BeginInvoke метод без параметров. Вызов RichTextBox в вашем примере имеет этот приоритет.

Но ваш, TextBlock который привязан к свойству и не обновляется вручную, имеет более низкий приоритет DataBind (8) , поэтому он обновляется медленнее.

Чтобы ускорить привязку, вы можете уменьшить приоритет вызова до RichTextBox и установить значение ниже 8, например Render (7) .

 Dispatcher.Invoke(/*...*/, DispatcherPriority.Render);
  

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

Продолжайте снижать приоритет:

 Dispatcher.Invoke(/*...*/, DispatcherPriority.Input);
  

Приложение реагирует лучше, но по-прежнему невозможно ввести что-либо в RichTextBox , пока оно заполнено текстом.

Следовательно, конечное значение равно Background (4) :

 Dispatcher.Invoke(new Action(() => { richTextBox1.AppendText("1"); }),
                  DispatcherPriority.Background);
  

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

1. Спасибо. Вам не обязательно использовать BeginInvoke , чтобы увидеть улучшение (фактически, когда вы выполняете два обновления, они полностью не синхронизированы друг с другом — текстовый блок завершается задолго до RichTextBox ). Я предполагаю, что диспетчер не может обрабатывать элементы в своей очереди достаточно быстро при использовании Invoke разрешения добавления элемента с более высоким приоритетом, прежде чем он сможет обработать обновление до RichTextBox .

2. Кроме того, я не совсем понимаю, зачем нам нужен, DispatcherPriority.Background а не просто какой-то приоритет, который ниже, чем у TextBlock обновления? Конечно, все, что имеет значение, — это их относительные приоритеты? Или в очереди диспетчера есть другие события, которые я не принимаю во внимание?

3. @flesh я использую BeginInvoke потому что этот вызов не ожидает завершения операции. Background Приоритет вызывает наименьшее количество проблем. Как я уже писал в ответе, вы можете использовать Render приоритет, который на одно число меньше DataBinding , но если вы попытаетесь щелкнуть где-нибудь, это не сработает, потому что события мыши имеют более низкий приоритет. Вы можете попробовать установить Input приоритет, а затем ввести что-то в RichTextBox , пока оно заполняется текстом. После этого вернитесь к Background приоритету и попробуйте ввести что-нибудь еще раз. Вы увидите разницу.