WPF RichTextBox 将插入符号定位到具有给定索引的可见字符
WPF RichTextBox position caret to visible character with given index
我一直在寻找 WPF 中的一种快速方法,以编程方式将光标设置为指定的可见符号索引。
问题是仅仅通过使用 Document.ContentStart.GetPositionAtOffset(cursorIndex, LogicalDirection.Forward)
我没有得到想要的结果,因为这个方法也计算不可见的符号 - 例如 Run
开始和 Run
结束符号.文档中几乎总是有一些标记符号,所以这总是以光标在所需位置之前结束。
那么,通过仅考虑可见符号,将插入符号定位到指定索引的快速、简单且优雅的方法是什么?
我想出了以下解决方案:
public virtual void SetCursorIndex(Int32 cursorIndex)
{
// If the specified index is less than or equal to 0, then we simply
// position the caret to the start of the document.
if (cursorIndex <= 0)
{
CaretPosition = Document.ContentStart;
return;
}
// If the specified index is greater than or equal to the total length, we simply
// position the caret to the end of the document.
String fullText = new TextRange(Document.ContentStart, Document.ContentEnd).Text;
Int32 totalTextLength = fullText.Length;
if (cursorIndex >= totalTextLength)
{
CaretPosition = Document.ContentEnd;
return;
}
// (*)
TextPointer endPtr = Document.ContentStart
.GetPositionAtOffset(cursorIndex, LogicalDirection.Forward);
TextRange range = new TextRange(Document.ContentStart, endPtr);
Int32 diff = cursorIndex - range.Text.Length;
while (diff != 0)
{
endPtr = endPtr.GetPositionAtOffset(diff, LogicalDirection.Forward);
range = new TextRange(Document.ContentStart, endPtr);
diff = cursorIndex - range.Text.Length;
// Overindexing, in this case we went over the document's length so we
// position the caret to the end of the document as a safety measure.
if (diff < 0)
{
endPtr = Document.ContentEnd;
break;
}
}
CaretPosition = endPtr;
}
// (*)
之前的部分是不言自明的。从那里,我们执行以下操作:
- 我们使用内置机制在
cursorIndex
逻辑位置(相对于文档的开头)获取文本指针 - 同样,这包括不可见的符号。但如果是这样,我们就不能比所需的可见字符索引更远,只能在它之前。如果它不包含任何不可见的符号,那么此方法会为我们提供一个 TextPointer
,它位于我们想要的位置。
- 我们创建了一个
TextRange
对象,它以文档开头和之前创建的 TextPointer
为界。
- 我们计算
TextRange
对象中文本的 cursorIndex
和 Length
的差异。这是我们迭代推进指针直到差为 0 的量。这是一种简单的启发式方法,比迭代推进 1 快一点。它基于以下事实:如果 TextRange
对象包含任何不可见符号,那么可见符号的数量永远不会大于我们必须推进 endPtr
TextPointer
的位置数,所以我们这样做 - 我们将 endPtr
推进cursorIndex
和 range.Length
的区别。如果在所需位置和 endPtr
指向的当前位置之间有任何不可见符号,那么我们仍然不会完全到达所需位置 - 这就是为什么我们在 [= 中进行 endPtr
的推进27=]-循环测试range
和cursorIndex
包含的文本长度差为0.
我一直在寻找 WPF 中的一种快速方法,以编程方式将光标设置为指定的可见符号索引。
问题是仅仅通过使用 Document.ContentStart.GetPositionAtOffset(cursorIndex, LogicalDirection.Forward)
我没有得到想要的结果,因为这个方法也计算不可见的符号 - 例如 Run
开始和 Run
结束符号.文档中几乎总是有一些标记符号,所以这总是以光标在所需位置之前结束。
那么,通过仅考虑可见符号,将插入符号定位到指定索引的快速、简单且优雅的方法是什么?
我想出了以下解决方案:
public virtual void SetCursorIndex(Int32 cursorIndex)
{
// If the specified index is less than or equal to 0, then we simply
// position the caret to the start of the document.
if (cursorIndex <= 0)
{
CaretPosition = Document.ContentStart;
return;
}
// If the specified index is greater than or equal to the total length, we simply
// position the caret to the end of the document.
String fullText = new TextRange(Document.ContentStart, Document.ContentEnd).Text;
Int32 totalTextLength = fullText.Length;
if (cursorIndex >= totalTextLength)
{
CaretPosition = Document.ContentEnd;
return;
}
// (*)
TextPointer endPtr = Document.ContentStart
.GetPositionAtOffset(cursorIndex, LogicalDirection.Forward);
TextRange range = new TextRange(Document.ContentStart, endPtr);
Int32 diff = cursorIndex - range.Text.Length;
while (diff != 0)
{
endPtr = endPtr.GetPositionAtOffset(diff, LogicalDirection.Forward);
range = new TextRange(Document.ContentStart, endPtr);
diff = cursorIndex - range.Text.Length;
// Overindexing, in this case we went over the document's length so we
// position the caret to the end of the document as a safety measure.
if (diff < 0)
{
endPtr = Document.ContentEnd;
break;
}
}
CaretPosition = endPtr;
}
// (*)
之前的部分是不言自明的。从那里,我们执行以下操作:
- 我们使用内置机制在
cursorIndex
逻辑位置(相对于文档的开头)获取文本指针 - 同样,这包括不可见的符号。但如果是这样,我们就不能比所需的可见字符索引更远,只能在它之前。如果它不包含任何不可见的符号,那么此方法会为我们提供一个TextPointer
,它位于我们想要的位置。 - 我们创建了一个
TextRange
对象,它以文档开头和之前创建的TextPointer
为界。 - 我们计算
TextRange
对象中文本的cursorIndex
和Length
的差异。这是我们迭代推进指针直到差为 0 的量。这是一种简单的启发式方法,比迭代推进 1 快一点。它基于以下事实:如果TextRange
对象包含任何不可见符号,那么可见符号的数量永远不会大于我们必须推进endPtr
TextPointer
的位置数,所以我们这样做 - 我们将endPtr
推进cursorIndex
和range.Length
的区别。如果在所需位置和endPtr
指向的当前位置之间有任何不可见符号,那么我们仍然不会完全到达所需位置 - 这就是为什么我们在 [= 中进行endPtr
的推进27=]-循环测试range
和cursorIndex
包含的文本长度差为0.