Ваши две основные причины проблем с производительностью:
- Вы вызываете selection.GetPropertyValue () больше раз, чем необходимо
- Вы пересчитываете каждый раз, когда выбор изменяется
Метод GetPropertyValue () должен внутренне сканировать каждый элемент в документе, что делает его медленным.Поэтому вместо того, чтобы вызывать его несколько раз с одним и тем же аргументом, сохраните возвращаемые значения:
private void HandleSelectionChange()
{
var family = selection.GetPropertyValue(FontFamilyProperty);
var weight = selection.GetPropertyValue(FontWeightProperty);
var style = selection.GetPropertyValue(FontStyleProperty);
var align = selection.GetPropertyValue(Paragraph.TextAlignmentProperty);
var unset = DependencyProperty.UnsetValue;
SelectionFontFamily = family!=unset ? (FontFamily)family : null;
SelectionIsBold = weight!=unset && (FontWeight)weight == FontWeight.Bold;
SelectionIsItalic = style!=unset && (FontStyle)style == FontStyle.Italic;
SelectionIsLeftAligned = align!=unset && (TextAlignment)align == TextAlignment.Left;
SelectionIsCenterAligned = align!=unset && (TextAlignment)align == TextAlignment.Center;
SelectionIsRightAligned = align!=unset && (TextAlignment)align == TextAlignment.Right;
SelectionIsJustified = align!=unset && (TextAlignment)align == TextAlignment.Justify;
}
Это будет примерно в 3 раза быстрее, но для того, чтобы конечный пользователь чувствовал себя очень быстро, не обновляйтенастройки мгновенно при каждом изменении.Вместо этого обновите ContextIdle:
bool _queuedChange;
private void richTextBox_SelectionChanged(object sender, RoutedEventArgs e)
{
if(!_queuedChange)
{
_queuedChange = true;
Dispatcher.BeginInvoke(DispatcherPriority.ContextIdle, (Action)(() =>
{
_queuedChange = false;
HandleSelectionChange();
}));
}
}
Это вызывает метод HandleSelctionChanged()
(см. Выше) для фактической обработки изменения выбора, но задерживает вызов до приоритета диспетчера ContextIdle, а также ставит в очередь только одно обновление независимо от того, скольконаступают события изменения выбора.
Возможны дополнительные ускорения
Приведенный выше код превращает все четыре GetPropertyValue в одну операцию DispatcherOperation, что означает, что у вас все еще может быть «задержка»пока четыре звонка.Чтобы уменьшить отставание еще в 4 раза, сделайте только один GetPropertyValue для DispatcherOperation.Так, например, первая операция DispatcherOperation вызовет функцию GetPropertyValue (FontFamilyProperty), сохранит результат в поле и запланирует следующую операцию DispatcherOperation для получения веса шрифта.Каждая последующая операция DispatcherOperation будет выполнять то же самое.
Если этого дополнительного ускорения по-прежнему недостаточно, следующим шагом будет разделение выделения на более мелкие фрагменты, вызов GetPropertyValue для каждого фрагмента в отдельной DispatcherOperation, а затем объединение результатов.вы получаете.
Чтобы получить абсолютную максимальную плавность, вы можете реализовать свой собственный код для GetPropertyValue (просто итерируйте ContentElements в выделении), который работает постепенно и возвращает после проверки, скажем, 100 элементов.В следующий раз, когда вы это называете, он продолжит с того места, где остановился.Это гарантировало бы вашу способность предотвращать любое заметное отставание, изменяя объем работы, выполняемой за DispatcherOperation.
Помогло бы многопоточность?
Вы спрашиваете в комментариях, является ли этоможно сделать с помощью потоков.Ответ в том, что вы можете использовать поток для организации работы, но так как вы всегда должны возвращать Dispatcher.Invoke в основной поток для вызова GetPropertyValue, вы все равно будете блокировать ваш поток пользовательского интерфейса на всю продолжительность каждого вызова GetPropertyValue, поэтому его гранулярностьвсе еще проблемаДругими словами, многопоточность на самом деле ничего не дает, кроме возможности избежать использования конечного автомата для разбиения вашей работы на куски размером с кусочек.