如何在 C# WPF 应用程序中显示格式化文本?

How to display formatted text in C# WPF application?

我有一个旧的第 3 方系统的代码文本文件,我正在尝试升级。该代码是结构化文本,看起来与 VB 非常相似。我想在 WPF 应用程序中解析文本文件并显示格式化文本。理想情况下,它看起来类似于 Visual Studio 代码编辑器。

下面是我尝试格式化的代码示例

--this is a comment
LOCAL tag1 --LOCAL would be formatted
LOCAL tag2
LOCAL foo
IF tag1 > tag2 THEN --IF and THEN would be formatted
   foo = tag1
END IF --end if would be formatted

我已经设法通过从代码的原始文本创建一个 FlowDocument 来做到这一点。然后我在文本文件中搜索关键字并使用以下方法更改文本的颜色

        private FlowDocument FormatDocument(FlowDocument flowDocument, List<string> keyWordList, Brush brush)
        {
            TextPointer position = flowDocument.ContentStart;
            while (position != null)
            {
                if (position.CompareTo(flowDocument.ContentEnd) == 0)
                    break;

                if (position.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.Text) //checks to see if textpointer is actually text
                {
                    foreach (string keyword in keyWordList)
                    {
                        string textRun = position.GetTextInRun(LogicalDirection.Forward);
                        string pattern = @"\b" + Regex.Escape(keyword) + @"\b";
                        Match match = Regex.Match(textRun, pattern, RegexOptions.IgnoreCase);

                        if (match.Success)
                        {
                            int indexInRun = match.Index;
                            int indexOfComment = textRun.IndexOf("--");
                            TextPointer startPosition = position.GetPositionAtOffset(indexInRun);
                            TextPointer endPosition = startPosition.GetPositionAtOffset(keyword.Length);
                            TextRange keywordRange = new TextRange(startPosition, endPosition);
                            string test = keywordRange.Text;
                            if (indexOfComment == -1 || indexInRun < indexOfComment)
                                keywordRange.ApplyPropertyValue(TextElement.ForegroundProperty, brush);
                        }
                    }
                    position = position.GetNextContextPosition(LogicalDirection.Forward);
                }
                else //If the current position doesn't represent a text context position, go to the next context position.
                    position = position.GetNextContextPosition(LogicalDirection.Forward); // This can effectively ignore the formatting or embed element symbols.
            }
            return flowDocument;
        }

当文件很大时,代码有点慢,所以我想知道是否有更好的方法来解决这个问题?

您的代码似乎没问题,只是您在每个循环的每次迭代中都创建了一堆对象,这会很慢,尤其是 对于 Regex 对象。如果你编译它们,正则表达式也会快得多。在任一循环之外创建您的 Regex 对象并编译它们,我敢打赌您会看到一些改进。

如果这还不够改进,请尝试构建一个匹配关键字列表中任何单词的正则表达式 (\b[keyword1|keyword2|keyword3|...]\b)。

public static FlowDocument FormatDocument(FlowDocument flowDocument,
                                            List<string> keyWordList,
                                            Brush brush)
{
    var regexForKeyword = keyWordList.ToDictionary(k => k,
                                                   k => new Regex(@"\b" + Regex.Escape(keyword) + @"\b",
                                                                  RegexOptions.Compiled | RegexOptions.IgnoreCase));

    var position = flowDocument.ContentStart;
    while (position != null)
    {
        if (position.CompareTo(flowDocument.ContentEnd) == 0)
            break;

        if (position.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.Text) //checks to see if textpointer is actually text
        {
            foreach (string keyword in keyWordList)
            {
                var textRun = position.GetTextInRun(LogicalDirection.Forward);
                var match = regexForKeyword[keyword].Match(textRun);

                if (match.Success)
                {
                    var indexInRun = match.Index;
                    var indexOfComment = textRun.IndexOf("--");
                    var startPosition = position.GetPositionAtOffset(indexInRun);
                    var endPosition = startPosition.GetPositionAtOffset(keyword.Length);
                    var keywordRange = new TextRange(startPosition, endPosition);
                    var test = keywordRange.Text;
                    if (indexOfComment == -1 || indexInRun < indexOfComment)
                        keywordRange.ApplyPropertyValue(TextElement.ForegroundProperty, brush);
                }
            }
            position = position.GetNextContextPosition(LogicalDirection.Forward);
        }
        else //If the current position doesn't represent a text context position, go to the next context position.
            position = position.GetNextContextPosition(LogicalDirection.Forward); // This can effectively ignore the formatting or embed element symbols.
    }
    return flowDocument;
}