【问题标题】:Select Range of Text in WPF RichTextBox (FlowDocument) Programmatically以编程方式选择 WPF RichTextBox(FlowDocument)中的文本范围
【发布时间】:2010-11-30 01:54:12
【问题描述】:

我有这个 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 where "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);
}

【问题讨论】:

    标签: c# wpf richtextbox


    【解决方案1】:
        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);
        }
    

    【讨论】:

    • 请始终将您的答案放在上下文中,而不仅仅是粘贴代码。有关详细信息,请参阅here
    【解决方案2】:
        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;
        }
    

    【讨论】:

    • 欢迎来到 SO 并感谢您发布答案。请考虑扩展您的答案以包括对您的代码的解释。
    【解决方案3】:

    很长一段时间都找不到性能可接受的解决方案来解决这个问题。下一个示例在我的情况下具有最高性能。希望它也能帮助别人。

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

    【讨论】:

      【解决方案4】:

      我的版本基于 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()ing 的情况下格式化文本:

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

      【讨论】:

        【解决方案5】:

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

        【讨论】:

        • 这对我有用 - 您需要在“return ret.GetPositionAtOffset(i-1, LogicalDirection.Forward)”之后添加一个分号 - 我尝试编辑,但编辑少于 6 个字符(捂脸)
        【解决方案6】:
        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
        

        试试这个,这应该返回给定字符偏移量的文本指针。 (对不起,它在 VB 中,但这就是我正在从事的工作......)

        【讨论】:

        • 不错!我得到了该代码的一个版本,将其添加到问题中。干杯。
        • 这对于计算 RichTextBox 中的字符也很方便:只需在 out 不为空时执行循环并在最后返回 i
        • 这种方法可以让我获得任何指定标记之后的每个字符在我的令牌“粗体”之后,我只需要它来操作我的令牌!
        • 就像 Visual Studio 使用保留的关键字一样!
        • 这不适用于单个字符或两个相邻元素。
        【解决方案7】:

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

        <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 中单击或执行选择,您的选择就会消失。

        【讨论】:

          【解决方案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);
          

          【讨论】:

          • @Tomas 不适合我,恐怕。使用该代码为我选择/着色字母 2-6。我要试试别的,然后回到这里。
          • 谢谢你——我知道我要么必须设置.Selection,要么打电话给.Select(...)——但必须打电话给.Selection.Select?完全出乎意料。
          • 是的。这行得通,由于 MyRichTextBox 失焦,我遇到了问题。因此,如果有任何问题,首先以编程方式关注它,(MyRichTextBox.focus())。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-11-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多