C# 以编程方式选择WPF RichTextBox(FlowDocument)中的文本范围

C# 以编程方式选择WPF RichTextBox(FlowDocument)中的文本范围,c#,wpf,richtextbox,C#,Wpf,Richtextbox,我有一个WPF RichTextBox,我想通过编程选择给定范围的字母/单词并突出显示它。我已经试过了,但它不起作用,可能是因为我没有考虑一些隐藏的FlowDocument标签或类似的内容。例如,我想选择字母3-8,但选择了字母2-6): 我意识到RichTextBox的处理比我想象的要复杂一些:) 更新:我在MSDN论坛上得到了一些答案:“dekurver”seid: 您正在指定的偏移不正确 字符偏移,但符号偏移。 你需要做的是找一个 您知道的文本指针是相邻的 到文本,然后可以添加字符 补偿

我有一个WPF RichTextBox,我想通过编程选择给定范围的字母/单词并突出显示它。我已经试过了,但它不起作用,可能是因为我没有考虑一些隐藏的FlowDocument标签或类似的内容。例如,我想选择字母3-8,但选择了字母2-6):

我意识到RichTextBox的处理比我想象的要复杂一些:)

更新:我在MSDN论坛上得到了一些答案:“dekurver”seid:

您正在指定的偏移不正确 字符偏移,但符号偏移。 你需要做的是找一个 您知道的文本指针是相邻的 到文本,然后可以添加字符 补偿

“LesterLobo”说:

您将需要通过 段落和内联以查找 然后在循环中单击它们的偏移 申请 具体文本。请注意,当您编辑 你的文字会移动,但你的 高亮显示不会移动,因为其 与偏移量关联,而不是与 文本。但是,您可以创建一个 自定义运行,并为 它

如果有人知道如何处理流程文档,我仍然希望看到一些示例代码

编辑我得到了一个版本的Kratz VB代码,它看起来像这样:

private static TextPointer GetPoint(TextPointer start, int x)
{
    var ret = start;
    var i = 0;
    while (i < x && ret != null)
    {
        if (ret.GetPointerContext(LogicalDirection.Backward) == 
TextPointerContext.Text ||
            ret.GetPointerContext(LogicalDirection.Backward) == 
TextPointerContext.None)
            i++;
        if (ret.GetPositionAtOffset(1, 
LogicalDirection.Forward) == null)
            return ret;
        ret = ret.GetPositionAtOffset(1, 
LogicalDirection.Forward);
    }
    return ret;
}
Colorize(item.Offset, item.Text.Length, Colors.Blue);

private void Colorize(int offset, int length, Color color)
{
    var textRange = MyRichTextBox.Selection;
    var start = MyRichTextBox.Document.ContentStart;
    var startPos = GetPoint(start, offset);
    var endPos = GetPoint(start, offset + length);

    textRange.Select(startPos, endPos);
    textRange.ApplyPropertyValue(TextElement.ForegroundProperty, 
new SolidColorBrush(color));
    textRange.ApplyPropertyValue(TextElement.FontWeightProperty, 
FontWeights.Bold);
}
试试看:

var textRange = MyRichTextBox.Selection;
var start = MyRichTextBox.Document.ContentStart;
var startPos = start.GetPositionAtOffset(3);
var endPos = start.GetPositionAtOffset(8);
textRange.Select(startPos, endPos);
textRange.ApplyPropertyValue(TextElement.ForegroundProperty, new SolidColorBrush(Colors.Blue));
textRange.ApplyPropertyValue(TextElement.FontWeightProperty, FontWeights.Bold);
Public函数GoToPoint(ByVal start作为TextPointer,ByVal x作为Integer)作为TextPointer
当text指针=开始时变暗
尺寸i为整数=0
当我
尝试此操作,这将返回给定字符偏移量的文本指针。(很抱歉,这是在VB中,但这正是我正在使用的…

顺便说一句(这可能对我以外的所有人来说都是学术性的),如果您在RichTextBox的容器上设置FocusManager.IsFocusScope=“True”,例如网格

<Grid FocusManager.IsFocusScope="True">...</Grid>
我还没有尝试过RichTextBox,但是在FlowDocumentReader中模板化find TextBox时,它工作得非常好。只是为了确定你也可以设置

<RichTextBox FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}">...</RichTextBox>
。。。
确保RichTextBox在其焦点范围内具有焦点


当然,这样做的缺点是,如果用户在RichTextBox中单击或执行选择,您的选择就会消失。

我尝试使用KratzVB发布的解决方案,但发现它忽略了换行符。如果要计数\r和\n符号,则此代码应起作用:

private static TextPointer GetPoint(TextPointer start, int x)
{

        var ret = start;
        var i = 0;
        while (ret != null)
        {
            string stringSoFar = new TextRange(ret, ret.GetPositionAtOffset(i, LogicalDirection.Forward)).Text;
            if (stringSoFar.Length == x)
                    break;
            i++;
            if (ret.GetPositionAtOffset(i, LogicalDirection.Forward) == null)
                return ret.GetPositionAtOffset(i-1, LogicalDirection.Forward)

        }
        ret=ret.GetPositionAtOffset(i, LogicalDirection.Forward);
        return ret;
}

我的版本基于洞穴居住者的版本

private static TextPointer GetPositionAtCharOffset(TextPointer start, int numbertOfChars)
{
    var offset = start;
    int i = 0;
    string stringSoFar="";
    while (stringSoFar.Length < numbertOfChars)
    {
        i++;
        TextPointer offsetCandidate = start.GetPositionAtOffset(
                i, LogicalDirection.Forward);

        if (offsetCandidate == null)
            return offset; // ups.. we are to far

        offset = offsetCandidate;
        stringSoFar = new TextRange(start, offset).Text;
    }

    return offset;
}
而不是这个(慢):

你应该这样做(更快)

或创建单独的方法以获取TextRange:

private static TextRange GetTextRange(TextPointer start, int startIndex, int length)
{
    var rangeStart = GetPositionAtCharOffset(start, startIndex);
    var rangeEnd = GetPositionAtCharOffset(rangeStart, length);
    return new TextRange(rangeStart, rangeEnd);
}
您现在可以格式化文本,而无需
Select()
ing:

var range = GetTextRange(Document.ContentStart, 3, 8);
range.ApplyPropertyValue(
    TextElement.BackgroundProperty, 
    new SolidColorBrush(Colors.Aquamarine));

很长一段时间无法找到具有可接受性能的解决方案解决此问题。下一个示例适用于我的情况,具有最高的性能。希望它也能帮助别人

TextPointer startPos = rtb.Document.ContentStart.GetPositionAtOffset(searchWordIndex, LogicalDirection.Forward);
startPos = startPos.CorrectPosition(searchWord, FindDialog.IsCaseSensitive);
if (startPos != null)
{
    TextPointer endPos = startPos.GetPositionAtOffset(textLength, LogicalDirection.Forward);
    if (endPos != null)
    {
         rtb.Selection.Select(startPos, endPos);
    }
}

public static TextPointer CorrectPosition(this TextPointer position, string word, bool caseSensitive)
{
   TextPointer start = null;
   while (position != null)
   {
       if (position.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.Text)
       {
           string textRun = position.GetTextInRun(LogicalDirection.Forward);

           int indexInRun = textRun.IndexOf(word, caseSensitive ? StringComparison.InvariantCulture : StringComparison.InvariantCultureIgnoreCase);
           if (indexInRun >= 0)
           {
               start = position.GetPositionAtOffset(indexInRun);
               break;
           }
       }

       position = position.GetNextContextPosition(LogicalDirection.Forward);
   }

   return start; 
}
private TextPointer GetPoint(TextPointer start,int pos)
{
var-ret=开始;
int i=0;
while(i
@Tomas恐怕不适合我。字母2-6使用该代码为我选择/着色。我要试试别的东西回来。谢谢你——我知道我要么要设置
.Selection
,要么要调用
.Select(…)
——但必须调用
.Selection.Select
?完全出乎意料,是的。这项工作,我有一个问题,由于MyRichTextBox的重点。因此,如果有任何问题,首先以编程方式关注它,(MyRichTextBox.focus())。很好!我得到了一个版本的代码,将它添加到问题中。干杯。这对于计算RichTextBox中的字符也很方便:只需在
out
不为null时执行循环,并在结尾返回
i
。此方法可以让我获得任何指定标记后面的每个字符,我只需要它来获取指向我的单词的指针,因为当我尝试将其设置为粗体时,例如,它就是在我的代币“bold”后生成整个句子我只需要它来操作我的代币!就像VisualStudio使用其保留的关键字一样!这不适用于单个字符或两个相邻的元素。这适用于我-您需要在“return ret.GetPositionAtOffset(i-1,LogicalDirection.Forward)”后添加分号-我尝试编辑,但编辑长度小于6个字符(facepalm)。欢迎使用SO,感谢您发布答案。请考虑扩展您的答案,包括对代码的解释。请将您的答案始终放在上下文中,而不只是粘贴代码。有关更多详细信息,请参阅。
var startPos = GetPoint(start, offset);
var endPos = GetPoint(start, offset + length);
var startPos = GetPoint(start, offset);
var endPos = GetPoint(startPos, length);
private static TextRange GetTextRange(TextPointer start, int startIndex, int length)
{
    var rangeStart = GetPositionAtCharOffset(start, startIndex);
    var rangeEnd = GetPositionAtCharOffset(rangeStart, length);
    return new TextRange(rangeStart, rangeEnd);
}
var range = GetTextRange(Document.ContentStart, 3, 8);
range.ApplyPropertyValue(
    TextElement.BackgroundProperty, 
    new SolidColorBrush(Colors.Aquamarine));
TextPointer startPos = rtb.Document.ContentStart.GetPositionAtOffset(searchWordIndex, LogicalDirection.Forward);
startPos = startPos.CorrectPosition(searchWord, FindDialog.IsCaseSensitive);
if (startPos != null)
{
    TextPointer endPos = startPos.GetPositionAtOffset(textLength, LogicalDirection.Forward);
    if (endPos != null)
    {
         rtb.Selection.Select(startPos, endPos);
    }
}

public static TextPointer CorrectPosition(this TextPointer position, string word, bool caseSensitive)
{
   TextPointer start = null;
   while (position != null)
   {
       if (position.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.Text)
       {
           string textRun = position.GetTextInRun(LogicalDirection.Forward);

           int indexInRun = textRun.IndexOf(word, caseSensitive ? StringComparison.InvariantCulture : StringComparison.InvariantCultureIgnoreCase);
           if (indexInRun >= 0)
           {
               start = position.GetPositionAtOffset(indexInRun);
               break;
           }
       }

       position = position.GetNextContextPosition(LogicalDirection.Forward);
   }

   return start; 
}
    private TextPointer GetPoint(TextPointer start, int pos)
    {
        var ret = start;
        int i = 0;
        while (i < pos)
        {
            if (ret.GetPointerContext(LogicalDirection.Forward) ==
    TextPointerContext.Text)
                i++;
            if (ret.GetPositionAtOffset(1, LogicalDirection.Forward) == null)
                return ret;
            ret = ret.GetPositionAtOffset(1, LogicalDirection.Forward);
        }
        return ret;
    }
    private void SelectText(int start, int length)
    {
        TextRange textRange = new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd);
        TextPointer pointerStart = textRange.Start.GetPositionAtOffset(start, LogicalDirection.Forward);
        TextPointer pointerEnd = textRange.Start.GetPositionAtOffset(start + length, LogicalDirection.Backward);

        richTextBox.Selection.Select(pointerStart, pointerEnd);
    }