如何在 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;
}
我有一个旧的第 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;
}