在wpf richtextbox中找到所需的字符串模式后,如何获得文本的正确位置(textpointer)

How to get correct position(textpointer) of text after finding desired string pattern in wpf richtextbox

提问人:livan3li 提问时间:10/22/2023 最后编辑:livan3li 更新时间:10/22/2023 访问量:32

问:

当用户在 wpf 的 richtextbox 中输入 Web URL 时,我正在尝试将超链接应用于给定的文本范围。 到目前为止,我已经找到了如何在给定给定的文本指针对象给定的情况下在运行时动态应用超链接。

基本上,我的问题是我无法正确定位richtextbox中的文本范围。我已经为此工作了整整两天。我尝试了很多不同的方法。我进入的最后阶段是代码。

我正在以相反的顺序遍历段落,以便更改不会影响下一个段落。 在将段落作为字符串获取后,我正在应用正则表达式并在任何匹配命中时获取匹配文本的索引。 然后我正在通过应用来验证我从匹配中获得的文本范围。(见下文;mat.index、mat.length)new TextRange(sp, ep).Text

但关键是,我从正则表达式中获得的索引位置有时与我从上面提到的 TextRange 获得的索引位置不匹配。由于这个原因,我试图通过一种丑陋的(启发式方法)来解决这个问题。 经过一些调试和深入挖掘,我意识到这些匹配项的末尾或之前的空格会导致程序崩溃。奇怪的是,如果我删除空格,它可以正常工作,否则会在行中抛出异常System.ExecutionEngineException: 'Exception of type 'System.ExecutionEngineException' was thrown.'new Hyperlink(sp, ep).Click += Match_link_Click;

因此,我怎样才能找到匹配文本并获取为文本指针。

private void DetectAndMarkHyperlinks()
{
    foreach (Paragraph item in rtbxTextArea.Document.Paragraphs().Reverse())
    {
        string paragraph = new TextRange(item.ContentStart, item.ContentEnd).Text; 
        //find all web adresses/urls by given regex pattern.
        MatchCollection matches = _regex.Matches(paragraph);
        if(matches.Count > 0)
        {
            foreach (Match mat in matches.Reverse())
            {
                // sp is start pointer of match
                // ep is end of the pointer of the match

                TextPointer sp, ep;
                int len = 0, start = 0, prev_diff;
                string last_text = "";
                do
                {
                    // get start position of match in the paragraph in document scale.
                    sp = item.ContentStart.GetPositionAtOffset(mat.Index + start, LogicalDirection.Forward
                    // get end of the position of match in the paragraph in document scale.
                    ep = sp.GetPositionAtOffset(mat.Length + len, LogicalDirection.Forward);
                    prev_diff = len; // backup 
                    len = mat.Value.Length - (last_text = new TextRange(sp, ep).Text).Length;

                    // if the text we got from given range is same as match found by regex.
                    // if not same then next iteration comes in;
                    // which "len" is used to determine the difference between range text and match text
                    // to locate textpointer correctly.
                    if(last_text.Length == mat.Value.Length)
                    {
                        // if the text from given range and match are same
                        if (last_text != mat.Value)
                        {
                            // even if length is same there might be glide
                            // so we are sliding by increasing start and end of indexes same amount.

                            for (int ch = 0; ch < last_text.Length; ch++)
                                if (last_text[ch] == mat.Value[0])
                                    // make sure first two char are same.
                                    if (last_text[ch + 1] == mat.Value[1])
                                    {
                                        len = (prev_diff += (start = ch));
                                        break;
                                    }
                        }
                        else
                        {
                            //navigateUri is not set since user might not enter full uri.
                            new Hyperlink(sp, ep).Click += Match_link_Click; 
                            break;
                        }
                    }
                }
            }
        }
    }
}


private void Match_link_Click(object sender, RoutedEventArgs e)
{
    var hyperlink = (Hyperlink)sender;

    Process.Start(
        new ProcessStartInfo
        {
            FileName =  hyperlink.GetText(), 
            UseShellExecute = true
        });
}

这些是用于通过给定的 richtextbox 文档获取段落的扩展。

public static IEnumerable<Paragraph> Paragraphs(this FlowDocument doc)
{
    return doc.Descendants().OfType<Paragraph>();
}

public static IEnumerable<DependencyObject> Descendants(this DependencyObject root)
{
    if (root == null)
        yield break;
    yield return root;
    foreach (var child in LogicalTreeHelper.GetChildren(root).OfType<DependencyObject>())
        foreach (var descendent in child.Descendants())
            yield return descendent;
}
C# .NET WPF 超链接 RichTextBox

评论


答: 暂无答案