Как отслеживать TextPointer в WPF RichTextBox?

Я пытаюсь получить голову вокруг класса TextPointer в WPF RichTextBox.

Я хотел бы смочь отслеживать их так, чтобы я мог связать информацию с областями в тексте.

Я в настоящее время работаю с очень простым примером, чтобы попытаться выяснить то, что продолжается. В событии PreviewKeyDown я храню позицию курсора, и затем в событии PreviewKeyUp я создаю TextRange на основе прежде и после позиций курсора. Вот пример кода, который иллюстрирует то, что я пытаюсь сделать:

// The caret position before typing
private TextPointer caretBefore = null;

private void rtbTest_PreviewKeyDown(object sender, KeyEventArgs e)
{
    // Store caret position
    caretBefore = rtbTest.CaretPosition;
}

private void rtbTest_PreviewKeyUp(object sender, KeyEventArgs e)
{
    // Get text between before and after caret positions
    TextRange tr = new TextRange(caretBefore, rtbTest.CaretPosition);
    MessageBox.Show(tr.Text);
}

Проблема состоит в том, что текст, который я получаю, является пробелом. Например, если бы я ввожу символ затем, я ожидал бы находить текст "a" в TextRange.

Кто-либо знает то, что идет не так, как надо? Это могло быть что-то очень простое, но я провел день, не добираясь нигде.

Я пытаюсь охватить новую технологию WPF, но найти, что RichTextBox в особенности является так сложным, что он делает даже выполнение простых вещей как это трудным. Если у кого-либо есть какие-либо ссылки, которые делают хорошее задание объяснения TextPointer, я ценил бы его, если можно сообщить мне.

8
задан Alan Spark 15 June 2010 в 14:35
поделиться

1 ответ

Когда вы добавляете и удаляете текст из FlowDocument, все TextPointers корректируют свое положение на основе ряда эвристик, предназначенных для того, чтобы они оставались как можно ближе к одному и тому же «месту».

Для удаления это просто: если TextPointer находится в удаленном тексте, он оказывается между символами, которые окружали удаленный текст. Но для вставок это не так просто: когда текст или другие элементы вставляются в FlowDocument точно в существующий TextPointer, должен ли TextPointer оказаться до или после вставленного текста? TextPointer имеет свойство под названием «LogicalDirection», которое управляет этим.

В вашем случае происходит то, что захватываемая позиция "caretBefore" является именно той позицией TextPosition, в которую вставлен напечатанный символ, а в ваших тестовых примерах вашим логическим направлением является LogicalDirection.Forward. Таким образом, когда символ вставлен, ваш "caretBefore" заканчивается после вставленного символа, который совпадает с TextPosition, давая вам пустой TextRange.

Как TextPointer получает назначенное ему LogicalDirection? Если вы щелкнете RichTextBox, чтобы установить положение каретки, щелчок интерпретируется как находящийся между двумя символами. Если фактическая точка, по которой вы щелкнули, была на втором символе, LogicalDirection устанавливается на Forward, но если фактическая точка, на которой вы щелкнули, была первым символом, LogicalDirection устанавливается на Backward.

Попробуйте этот эксперимент:

  1. Установите FontSize = "40" и предварительно заполните RichTextBox текстом "ABCD" в конструкторе.
  2. Щелкните справа от буквы B и введите "X" между буквой B. а C. LogicalDirection - Backward, так что ваш «beforeCaret» заканчивается перед «X», а ваш MessageBox показывает «X».
  3. Щелкните на левой стороне буквы C и введите «X» между буквами B и C. LogicalDirection - Forward, так что ваш «beforeCaret» заканчивается после «X», а ваше MessageBox пусто.

Такое поведение противоречит здравому смыслу: когда вы не знаете, что LogicalDirection существует, вы могли бы подумать, что щелчок по правой стороне B или левой стороне C даст вам точно такое же положение курсора.

Примечание. Самый простой способ визуализировать происходящее - запустить MessageBox.Show и вместо этого выполнить caretBefore.InsertTextInRun ("^");

Как добиться желаемого результата? LogicalDirection доступен только для чтения. Один из способов - использовать TextRange для принудительного создания TextPointer с LogicalDirection Backward:

caretBefore = new TextRange(caretBefore, caretBefore.DocumentEnd).Start;

Сделайте это в PreviewKeyDown. Если вы дождетесь PreviewKeyUp, будет уже слишком поздно: caretBefore переместился. Это работает, потому что, насколько я могу судить, начало непустого TextRange всегда имеет LogicalDirection Backward.

Другой вариант - сохранить смещение символа от начала документа (обратите внимание, что это не смещение символа!).В этом случае вы можете сохранить смещение в PreviewKeyDown:

caretBeforeOffset = caretBefore.DocumentStart.OffsetToPosition(caretBefore);

и сбросить caretBefore на то же смещение символа в PreviewKeyUp:

caretBefore = caretBefore.DocumentStart.GetPositionAtOffset(caretBeforeOffset,
                                                            LogicalDirection.Forward);

Хотя это работает, это не так часто, как принуждение вашего TextPointer иметь LogicalDirection Backward: Любые изменения текста ранее в документе между PreviewKeyDown и PreviewKeyUp приведет к тому, что при вычислении смещения символа будет найдено неправильное местоположение, что в первую очередь было разработано для исправления TextPointers.

Я не знаю никаких хороших ресурсов для изучения TextPointers, кроме чтения документации и игры с ними, что вы уже делаете.

20
ответ дан 5 December 2019 в 08:22
поделиться
Другие вопросы по тегам:

Похожие вопросы: