RichTextBox - изменение размера пользовательского интерфейса приводит к огромной загрузке процессора

Недавно я разрабатывал RTF-редактор, который представляет собой простой UserControl, который имеет RichTextBox с несколькими событиями, такими как PreviewTextInput и PreviewMouseUp.

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

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

Сначала я заметил, что это удар производительности при выборе "Текст", поэтому вместо использования события SelectionChanged я решил использовать событие PreviewMouseUp, а затем выбрать Selection.

Затем после дальнейшего тестирования выяснилось, что изменение размера также вызвало огромные нагрузки. И я говорю о нагрузках от 5% до 30% с четырехъядерным процессором на 3,8 ГГц!

Чтобы еще раз проверить это, я решил прокомментировать мой RichTextBox и включить только новый RichTextBox без определенного свойства

<RichTextBox/>

Вставка этого в окно, заполнение текстом, а затем изменение размера окна, чтобы заставить алгоритм обертывания сделать то же самое, до 30% использования!

Я попытался исследовать этот вопрос, и большинство людей решили рекомендовать устанавливать значения PageWidth на высокие значения, чтобы предотвратить Wrapping:

richTextBox1.HorizontalScrollBarVisibility = ScrollBarVisibility.Visible;
richTextBox1.Document.PageWidth = 1000;

Что я не хочу, так как предыдущая версия Редактора, которую я написал, была сделана с помощью WinForms и могла легко сделать Wrapping, и я также хочу ее в новой версии WPF.

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

Мне немного грустно, потому что я люблю WPF, но я нашел тот или другой объект, который действительно неоптимизирован и/или не практичен по сравнению с копией WinForms, RichTextBox кажется еще одним из тех случаи: (

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

Ответ 1

Одним из способов преодоления этой проблемы может быть переход на режим "без обертки" при изменении размера окна, но когда пользователь закончил изменение размера - вернитесь в обычный режим. Затем алгоритм обертки будет выполняться только один раз в конце, и пользователи все равно должны иметь гладкое представление о вашем приложении. Пример кода:

public partial class MainWindow : Window
{
    public MainWindow() {
        InitializeComponent();
        this.SizeChanged += OnSizeChanged;
    }

    private Timer _timer;
    private void OnSizeChanged(object sender, SizeChangedEventArgs e) {
        // user started resizing - set large page width
        textBox.Document.PageWidth = 1000;
        // if we already setup timer - stop it and start all over
        if (_timer != null) {
            _timer.Dispose();
            _timer = null;
        }

        _timer = new Timer(_ => {
            // this code will run 100ms after user _stopped_ resizing
            Dispatcher.Invoke(() =>
            {
                // reset page width back to allow wrapping algorithm to execute
                textBox.Document.PageWidth = double.NaN;
            });
        }, null, 100, Timeout.Infinite);
    }
}