Textpointer GetTextInRun,在ö,ü或/和ä

时间:2020-10-27 15:53:00

标签: c# find rtf

我尝试在rtf中找到一个字符串(占位符)(标记并稍后替换)。它可以工作,但是如果有“ö”,“ä”,“ü” GetTextInRun可以将其分为两个或更多部分

TextRange text = new TextRange(rtb.Document.ContentStart, rtb.Document.ContentEnd);
            TextPointer current = text.Start.GetInsertionPosition(LogicalDirection.Forward);
            while (current != null)
            {

              string textInRun = current.GetTextInRun(LogicalDirection.Forward);
             ...
}

MSDN的意思是:“此方法仅返回文本的不间断运行。如果除Text以外的任何符号类型在指定方向上与当前TextPointer相邻,则不返回任何内容。类似地,仅返回直到下一个非文本的文本符号。”

那么öüä不是“文字”吗?但是为什么第二部分的结果不是“ä”而是“äöü”

占位符看起来像这样:“ [[tabletyp1的$ [TabelleTyp1_test]]”(一直有效),但可以是“ [[$ TabelleTyp1_1.HalloüchbinänÜmlütöst]]”(失败,textinRun:“ [[$ TabelleTyp1_1。“您好”-但是在rtf文件中的每次更改之后,结果也都会更改(“ [[$ TabelleTyp1_1。“”),因此,我认为必须有一个不可见的char来破坏“文本”,如果öäü在文本里面吗?但是我不知道如何在不丢失Textpointer的情况下忽略或删除这个(随机?)字符。

顺便说一句:正则表达式的“检测”没有任何问题,因此它不能成为“隐藏”字符吗? regex:@“ [[[$ TabelleTyp1_ [a-zA-Z0-9äöü。] *]]”)); 唯一的区别是:对于正则表达式,我使用表单richtextbox,用于选择/替换xceedrichttextbox(wpf)

编辑:我将text.Text复制到剪贴板,并寻找隐藏的字符..没有隐藏的字符,只有CR / LF在行尾。

Edit2:“。”是错误的,只有ö,ä和ü

1 个答案:

答案 0 :(得分:1)

我认为段落中的文本分成不同部分的原因只有一个 - 上下文变化。当用户更改以下特征之一时会发生这种情况:

  • 输入语言
  • 字体名称
  • 字体样式
  • 前景色
  • 背景颜色等

我不考虑可以以编程方式进行分区的情况。 顺便说一下,在 MS Office Word 中很容易重现这个问题。只需用英语输入单词 “test”,然后用另一种语言(例如德语)替换字母 “e”。然后运行英语拼写检查。你会看到一个错误。视觉上一切都是一样的。但是一个词会被分成三个RunMS Word 将检测来自另一种语言的字母并报告错误。

<块引用>

这个问题描述的问题很可能是输入 语。请参阅以下描述类似问题的帖子:Edit RichTextBox Run element without splitting into multiple Run elements

解决方案 1:

尝试在 RichTextBox 控件中设置输入语言。例如,德语:

<RichTextBox x:Name="rtb" Language="de-de">
</RichTextBox>

支持的文化代码 https://docs.microsoft.com/en-us/bingmaps/rest-services/common-parameters-and-types/supported-culture-codes

解决方案 2:

如果由于某些原因 RTF 文档已经包含拆分为不同 Run 的特定词,则有必要使用上下文相关的方法来解析/分析 RichTextBox 内容。

下面的例子展示了它是如何实现的。

MainWindow.xaml:

<Window ...
        Title="MainWindow" Height="350" Width="500">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <RichTextBox Name="rtb" BorderBrush="{x:Null}" Padding="5" Margin="10"
                     VerticalScrollBarVisibility="Auto">
            <FlowDocument>
                <Paragraph>
                        <Run>Paste some document for testing and press the button below</Run>
                </Paragraph>
            </FlowDocument>            
        </RichTextBox>
        <Button Grid.Row="1" Click="Button_SearchAsync" Margin="2" Padding="3">Press to search</Button>
    </Grid>
</Window>

MainWindow.xaml.cs:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;

namespace WpfApp17
{    
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }    

        private async void Button_SearchAsync(object sender, RoutedEventArgs e)
        {
            await FindWordsAsync(rtb);
            rtb.Focus();
        }

        public async Task FindWordsAsync(RichTextBox rtb)
        {
            await Task<object>.Factory.StartNew(() =>
            {
                var pairs = new Dictionary<string, SolidColorBrush>(StringComparer.InvariantCultureIgnoreCase)
                {
                    { "Rückvergrößerungsgeräten", Brushes.Blue },
                    { "Rückvergrößerungsgeräte", Brushes.DarkOrange },
                    { "Rückvergrößerungsgerät", Brushes.Red }
                };

                IList<KeyValuePair<string, TextRange>> ranges = null;

                this.Dispatcher.Invoke(() =>
                {
                    // 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
                    ranges = textRange.CalculateTextRange(pairs.Select(d => d.Key).ToList());
                });

                // Color words by using calculated `TextRange`.
                for (int i = 0; i < ranges.Count; i++)
                {
                    this.Dispatcher.Invoke(() => { ranges[i].Value.ApplyPropertyValue(TextElement.ForegroundProperty, pairs[ranges[i].Key]); });                    
                    //list[i].Value.ApplyPropertyValue(TextElement.FontSizeProperty, 20.0);                
                }
                return Task.FromResult<object>(null);
            });
        }
    }
}

TextRangeExt.cs:

using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Documents;

namespace WpfApp17
{
    public static class TextRangeExt
    {
        public static IList<KeyValuePair<string, TextRange>> CalculateTextRange(this 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");
            var regExp = new Regex(pattern.ToString(), RegexOptions.IgnoreCase);

            int correction = 0;
            var result = new List<KeyValuePair<string, TextRange>>();
            TextPointer start = range.Start;

            // Enumerate all found mathes and creating list of Word/TextRange pairs
            foreach (Match match in regExp.Matches(range.Text))
            {
                if (CalculateTextRange(start, match.Index - correction, match.Length) is TextRange tr)
                {
                    result.Add(new KeyValuePair<string, TextRange>(match.Value, tr));
                    correction = match.Index + match.Length;
                    start = tr.End;
                }
            }
            return result;
        }

        // Return calculated a `TextRange` of the string started from `iStart` index
        // and having `length` size or `null`.
        public static TextRange CalculateTextRange(TextPointer startSearch, int iStart, int length)
        {
            return (startSearch.GetTextPositionAtOffset(iStart) is TextPointer start)
                ? new TextRange(start, start.GetTextPositionAtOffset(length))
                : null;
        }

        public static TextPointer GetTextPositionAtOffset(this TextPointer position, int offset)
        {
            for (TextPointer current = position; current != null; current = position.GetNextContextPosition(LogicalDirection.Forward))
            {
                position = current;
                var adjacent = position.GetAdjacentElement(LogicalDirection.Forward);
                var navigator = position.GetPointerContext(LogicalDirection.Forward);
                switch (navigator)
                {
                    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--;
                        }
                        else if (adjacent is ListItem lsItem)
                        {
                            var index = new TextRange(lsItem.ElementStart, lsItem.ElementEnd).Text.IndexOf('\t');
                            if (index >= 0) offset -= index + 1;
                        }                       
                        break;

                    case TextPointerContext.ElementEnd:
                        if (adjacent is Paragraph para)
                        {                          
                            var correction = 0;
                            if (para.Parent is TableCell tcell)
                            {
                                var bCount = tcell.Blocks.Count;
                                var cellText = new TextRange(tcell.Blocks.FirstBlock.ContentStart, tcell.Blocks.LastBlock.ContentEnd).Text;

                                if ((bCount == 1 && cellText.EndsWith(Environment.NewLine)) || bCount > 1) 
                                {
                                    correction = 2;
                                }
                                else if (tcell.Parent is TableRow trow)
                                {
                                    var cells = trow.Cells.Count;
                                    correction = (cells <= 0 || trow.Cells.IndexOf(tcell) != cells - 1) ? 1 : 2;
                                } 
                            }
                            else
                            {
                                correction = 2;
                            }
                            offset -= correction;
                        }                                        
                        break;
                }
            }
            return position;
        }
    }
}

使用上面的代码或用于测试目的:

  1. FindWordsAsync() 方法中,用要在文档中找到的词填充 pairs。如果找到的话,设置一些颜色以用于更改单词的前景。但很可能您不需要此用于测试的功能,因此您应该更改代码以仅保留所需单词的列表。
  2. List<KeyValuePair<string, TextRange>> CalculateTextRange(this TextRange range, IList<string> words) 方法使用正则表达式来查找定义的单词。因此,此方法的一部分可以根据文档中的文本搜索条件进行更改。

enter image description here

相关问题