Как отслеживать 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", то я ожидаю найти текст" a " в TextRange.
кто-нибудь знает, что происходит? Это может быть что-то очень простое, но я провел день, ничего не добившись.
Я пытаюсь охватить новую технологию WPF, но обнаружил, что RichTextBox в частности настолько сложен, что он даже делать такие простые вещи, как это, трудно. Если у кого-то есть ссылки, которые хорошо объясняют TextPointer, я был бы признателен, если вы дадите мне знать.
1 ответов
когда вы добавляете и удаляете текст из FlowDocument, все TextPointers регулируют свое положение на основе ряда эвристик, предназначенных для того, чтобы они оставались как можно ближе к одному и тому же "месту".
для удаления это просто: если TextPointer находится в удаленном тексте, он заканчивается между символами, которые окружали удаленный текст. Но для вставок это не так просто: когда текст или другие элементы вставляются в FlowDocument точно в существующий TextPointer, должен ли TextPointer заканчиваться до или после вставленного текста? TextPointer имеет свойство LogicalDirection, которое управляет этим.
то, что происходит в вашем случае, заключается в том, что позиция "caretBefore", которую вы захватываете, - это именно текстовая позиция, в которую вставлен типизированный символ, а в ваших тестовых случаях LogicalDirection-LogicalDirection.Вперед. Таким образом, когда символ вставлен, ваш "caretBefore" заканчивается после вставленный символ, который совпадает с TextPosition давая вам пустой TextRange.
как TextPointer получает назначенный ему LogicalDirection? Если вы нажмете на RichTextBox, чтобы установить позицию курсора, щелчок интерпретируется как находящийся между двумя символами. Если фактическая точка вы нажали на второго персонажа, LogicalDirection в прямом, но если фактическая точка, по которой вы щелкнули был первый символ, LogicalDirection имеет значение Назад.
попробуйте этот эксперимент:
- установите FontSize= " 40 "и заполните RichTextBox текстом" ABCD " в конструкторе
- нажмите на правой стороне B и введите " X "между B и C. LogicalDirection назад, так что ваш" beforeCaret "заканчивается перед" X", и ваш MessageBox показывает"X".
- нажмите на левой стороне C и введите " X " между B и C. LogicalDirection вперед, так что ваш " beforeCaret "заканчивается после" X", и ваш MessageBox пуст.
это поведение противоречит интуиции: когда вы не знаете, что LogicalDirection существует, вы могли бы подумать, что нажатие на правую сторону B или левую сторону C даст вам точно такую же позицию каретки.
Примечание: простой способ визуализировать, что происходит, чтобы командовать из вашего MessageBox.Показать и вместо этого сделать caretBefore.InsertTextInRun("^");
как добиться результат вам нужен? LogicalDirection доступен только для чтения. Один способ заключается в использовании TextRange в силу строительства объекта textpointer с LogicalDirection отсталых:
caretBefore = new TextRange(caretBefore, caretBefore.DocumentEnd).Start;
сделайте это в PreviewKeyDown. Если вы ждете, пока PreviewKeyUp уже слишком поздно: caretBefore переехал. Это работает, потому что, насколько я могу судить, начало непустого TextRange всегда имеет LogicalDirection назад.
другой вариант-Сохранить смещение символа с начала документ (обратите внимание, что это не смещение символов!). В этом случае вы можете сохранить смещение в PreviewKeyDown:
caretBeforeOffset = caretBefore.DocumentStart.OffsetToPosition(caretBefore);
и сбросить caretBefore до того же смещения символа в PreviewKeyUp:
caretBefore = caretBefore.DocumentStart.GetPositionAtOffset(caretBeforeOffset,
LogicalDirection.Forward);
хотя это работает, это не так широко, как заставить ваш TextPointer иметь LogicalDirection назад: Любые изменения текста ранее в документе между PreviewKeyDown и PreviewKeyUp вызовут вычисление смещения символа, чтобы найти неправильное местоположение, это то, что TextPointers были разработаны, чтобы исправить в первую очередь.
Я не знаю никаких хороших ресурсов для изучения TextPointers, кроме чтения документации и игры с ними, что именно то, что вы уже делали.