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

时间:2009-09-21 13:10:21

标签: c# wpf richtextbox

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

var start = MyRichTextBox.Document.ContentStart;
var startPos = start.GetPositionAtOffset(3);
var endPos = start.GetPositionAtOffset(8);
var textRange = new TextRange(startPos,endPos);
textRange.ApplyPropertyValue(TextElement.ForegroundProperty,
    new SolidColorBrush(Colors.Blue));
textRange.ApplyPropertyValue(TextElement.FontWeightProperty, 
    FontWeights.Bold);

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

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

  

您指定的偏移量不是   字符偏移但符号偏移。   你需要做的是得到一个   你知道的TextPointer是相邻的   到文本,然后你可以添加字符   偏移。

而“LesterLobo”说:

  

你需要循环使用   段落和内联找到   接下来然后他们在一个循环中的偏移   申请所有的外观   具体文字。请注意,编辑时   你的文字会移动但是你的文字   突出显示不会像它一样移动   与偏移相关联而不是   文本。但是你可以创建一个   自定义运行并提供一个亮点   它...

如果有人知道他们对FlowDocuments的看法,他们仍然愿意看到一些示例代码...

编辑我得到了一个版本的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);
}

8 个答案:

答案 0 :(得分:11)

Public Function GoToPoint(ByVal start As TextPointer, ByVal x As Integer) As TextPointer
    Dim out As TextPointer = start
    Dim i As Integer = 0
    Do While i < x
        If out.GetPointerContext(LogicalDirection.Backward) = TextPointerContext.Text Or _
             out.GetPointerContext(LogicalDirection.Backward) = TextPointerContext.None Then
            i += 1
        End If
        If out.GetPositionAtOffset(1, LogicalDirection.Forward) Is Nothing Then
            Return out
        Else
            out = out.GetPositionAtOffset(1, LogicalDirection.Forward)
        End If


    Loop
    Return out
End Function

试试这个,这应该返回给定char偏移量的文本指针。 (对不起它在VB中,但那就是我在做什么...)

答案 1 :(得分:8)

试试:

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);

答案 2 :(得分:7)

我尝试使用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;
}

答案 3 :(得分:2)

我的版本基于cave_dweller的版本

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;
}

要省略某些字符,请在循环中添加此代码:

stringSoFar = stringSoFar.Replace("\r\n", "")
                         .Replace(" ", "")

而不是这个(慢):

var startPos = GetPoint(start, offset);
var endPos = GetPoint(start, offset + length);

你应该这样做(更快)

var startPos = GetPoint(start, offset);
var endPos = GetPoint(startPos, length);

或创建单独的方法来获取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()的情况下格式化文本:

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

答案 4 :(得分:2)

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

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; 
}

答案 5 :(得分:1)

顺便说一句(如果你在RichTextBox的容器上设置FocusManager.IsFocusScope =“True”,那么就可以学习除了我以外的所有人),例如Grid,

<Grid FocusManager.IsFocusScope="True">...</Grid>

那么你应该可以使用Johan Danforth的Colorize方法而不需要两次调用ApplyPropertyValue,RichTextBox应该使用默认选择Background和Foreground来突出显示选择。

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);
}

没有尝试使用RichTextBox,但在模板化FlowDocumentReader中查找TextBox时效果很好。只是为了确保你也可以设置

<RichTextBox FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}">...</RichTextBox>

确保RichTextBox在焦点范围内具有焦点。

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

答案 6 :(得分:0)

    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;
    }

答案 7 :(得分:0)

source
相关问题