C#WPF中的RichTextBox在执行期间更改位置

我正在尝试在WPF C#中创建语法着色器。为此,我需要获取每个单词的位置,并根据匹配的模式为其着色。 RichTextBox的文本选择系统非常复杂,因此我猜测检查给定单词在纯字符串中的位置并将其连接到文本中的位置会更容易。好吧,不取决于我已经更改了多少文字颜色,它的行为有所不同,有时甚至匹配错误的字母数。

示例:

using System.Windows;
using System.Windows.Documents;
using System.Windows.Media;

namespace Example
{
    /// <summary>
    ///     Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            var message = "Hello World and everyone here!";

            richTextBox.Document.Blocks.Clear();
            richTextBox.Document.Blocks.Add(new Paragraph(new Run(message)));

            var position = richTextBox.Document.ContentStart;

            var text = position.GetTextInRun(LogicalDirection.Forward);

            ShowPos(position,2,5); // shows message "Hello"
            ShowPos(position,8,5); // shows message "Wor" if upper line is not
                                   // commented - otherwise,shows "World"

        }

        private void ShowPos(TextPointer position,int from,int length)
        {
            var posA = position.GetPositionAtOffset(from);
            var posB = posA.GetPositionAtOffset(length);
            var range = new textrange(posA,posB);
            var textA = range.Text;

            //if removed,everything works fine
            range.ApplyPropertyValue(ForegroundProperty,brushes.DarkRed); 

            MessageBox.Show(textA);
        }
    }
}

非常令人沮丧。我该怎么做才能使每个单词都匹配并着色?

sunqianpeng 回答:C#WPF中的RichTextBox在执行期间更改位置

这是一篇很老的帖子,但也许有人会发现这个解决方案很有用。

不幸的是,搜索文本并确定其在 RichTextBox 中的位置是一个有点复杂的过程。由于文本可以包含不同的格式元素,例如背景/前景色、不同的字体、图像等,因此必须使用上下文来确定所需文本的位置。

一种可能的解决方案如下所示。

MainWindow.xaml

<Window ...
        Title="MainWindow" Height="250" Width="400"
        KeyDown="Window_KeyDown">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <RichTextBox Grid.Row="0" x:Name="rtb" AllowDrop="True" 
                     VerticalScrollBarVisibility="Auto" Padding="2" FontSize="18" 
                     HorizontalAlignment="Left" >
            <FlowDocument>
                <Paragraph>
                    <Run FontSize="10"  Text="In this example,you will learn to print Hello World." />
                    <Run Text="In this example,you will learn to print Hello World." />
                    <Run FontSize="20" Text="In this example,you will learn to print Hello World." />                   
                </Paragraph>
            </FlowDocument>
        </RichTextBox>

        <Button Grid.Row="1" Click="Button_Click">Color Words</Button>
    </Grid>
</Window> 

MainWindow.xaml.cs


private void Button_Click(object sender,RoutedEventArgs e)
{
    var pairs = new Dictionary<string,SolidColorBrush>(StringComparer.InvariantCultureIgnoreCase)
    {
        { "hello",Brushes.Red },{ "world",Brushes.Blue }
    };

    // Define the range to be used to analyze for the specified words
    var textRange = new TextRange(rtb.Document.ContentStart,rtb.Document.ContentEnd);

    // Build list of Word/TextRange pairs
    var list = CalculateTextRange(textRange,pairs.Select(d => d.Key).ToList());

    // Color words by using calculated `TextRange`.
    for (int i = 0; i < list.Count; i++)
    {
        list[i].Value.ApplyPropertyValue(TextElement.ForegroundProperty,pairs[list[i].Key]);
    }

    rtb.Focus();
}

public static IList<KeyValuePair<string,TextRange>> CalculateTextRange(TextRange range,IList<string> words)
{
    // Building the regular expression pattern 
    var pattern = new StringBuilder(@"\b");
    for (int i = 0; i < words.Count; i++)
    {
        if (i > 0) pattern.Append('|');
        pattern.Append(words[i]);
    }
    pattern.Append(@"\b");
    Regex regExp = new Regex(pattern.ToString(),RegexOptions.IgnoreCase);

    TextRange search = range;
    int correction = 0;
    var result = new List<KeyValuePair<string,TextRange>>();

    // Enumerate all found mathes and creating list of Word/TextRange pairs
    foreach (Match match in regExp.Matches(range.Text))
    {
        if (CalculateTextRange(search,match.Index - correction,match.Length) is TextRange tr)
        {
            result.Add(new KeyValuePair<string,TextRange>(match.Value,tr));
            correction = match.Index + match.Length;
            search = new TextRange(tr.End,search.End);
        }
    }
    return result;
}
        
// Calculates a `TextRange` of the string started from `iStart` index
// and having `length` size or `null`.
public static TextRange CalculateTextRange(TextRange search,int iStart,int length)
{
    return (GetTextPositionAtOffset(search.Start,iStart) is TextPointer start)
        ? new TextRange(start,GetTextPositionAtOffset(start,length))
        : null;
}

// Calculate `TextPointer` from defined text position by specified offset.
public static TextPointer GetTextPositionAtOffset(TextPointer position,int offset)
{
    for (TextPointer current = position; current != null; current = position.GetNextContextPosition(LogicalDirection.Forward))
    {
        position = current;
        var adjacent = position.GetAdjacentElement(LogicalDirection.Forward);
        var context = position.GetPointerContext(LogicalDirection.Forward);
        switch (context)
        {
            case TextPointerContext.Text:
                int count = position.GetTextRunLength(LogicalDirection.Forward);
                if (offset <= count)
                {
                    return position.GetPositionAtOffset(offset);
                }
                offset -= count;
                break;
            case TextPointerContext.ElementStart:
                if (adjacent is InlineUIContainer)
                    offset--;
                break;
            case TextPointerContext.ElementEnd:
                if (adjacent is Paragraph)
                    offset -= 2;
                break;
        }
    }
    return position;
}

上面的代码包含 Button_Click() 处理程序,当用户点击 Color Words 按钮时,它会执行以下步骤。

  1. 定义要在 RichTextBox 中找到的单词列表和列表中每个单词的颜色(在本例中实际上是画笔):
var pairs = new Dictionary<string,Brushes.Blue }
    }; 
  1. 定义用于分析指定词的范围。在此示例中,使用了整个 RichBoxText 文档:
var textRange = new TextRange(rtb.Document.ContentStart,rtb.Document.ContentEnd);
  1. 为列表中指定并在文档中找到的所有单词计算 TextRange 的列表:
// Build list of Word/TextRange pairs
var list = CalculateTextRange(textRange,pairs.Select(d => d.Key).ToList());
  1. 为所有找到的单词着色。

enter image description here

添加图片是为了检查即使在这种情况下也能正确计算位置。

本文链接:https://www.f2er.com/3117561.html

大家都在问