Here I present another Approach for highlighting text. I had a use case where I needed to decorate a bunch of C# Code in WPF, however I did not want to use textBlock.Inlines.Add type of syntax, instead I wanted to generate the highlighting XAML on the fly and then dynamically add it to a Canvas or some other container in WPF.
So suppose you want to colorize the following piece of code and also highlight a part of it:
public static void TestLoop(int count)
{
for(int i=0;i<count;i++)
Console.WriteLine(i);
}
Suppose the above code is found in a file called Test.txt .
Suppose you want to colorize all the C# keywords (public, static, void etc..) and simple types(int, string) in Blue, and Console.WriteLine highlight in yellow.
Step 0. Create a new WPF Application and include some sample code similar to above in a file called Test.txt
Step 1. Create a Code Highlighter class:
using System.IO;
using System.Text;
public enum HighLightType
{
Type = 0,
Keyword = 1,
CustomTerm = 2
}
public class CodeHighlighter
{
public static string[] KeyWords = { "public", "static", "void", "return", "while", "for", "if" };
public static string[] Types = { "string", "int", "double", "long" };
private string FormatCodeInXaml(string code, bool withLineBreak)
{
string[] mapAr = { "<","<" , //Replace less than sign
">",">" }; //Replace greater than sign
StringBuilder sb = new StringBuilder();
using (StreamReader sr = new StreamReader(new MemoryStream(Encoding.UTF8.GetBytes(code))))
{
while (!sr.EndOfStream)
{
string line = sr.ReadLine();
line = line.Replace("\t", "    "); //Replace tabs
line = line.Replace(" ", " "); //Replace spaces
for (int i = 0; i < mapAr.Length; i += 2)
line = line.Replace(mapAr[i], mapAr[i + 1]);
if (withLineBreak)
sb.AppendLine(line + "<LineBreak/>"); //Replace line breaks
else
sb.AppendLine(line);
}
}
return sb.ToString();
}
private string BuildForegroundTag(string highlightText, string color)
{
return "<Span Foreground=\"" + color + "\">" + highlightText + "</Span>";
}
private string BuildBackgroundTag(string highlightText, string color)
{
return "<Span Background=\"" + color + "\">" + highlightText + "</Span>";
}
private string HighlightTerm(HighLightType type, string term, string line)
{
if (term == string.Empty)
return line;
string keywordColor = "Blue";
string typeColor = "Blue";
string statementColor = "Yellow";
if (type == HighLightType.Type)
return line.Replace(term, BuildForegroundTag(term, typeColor));
if (type == HighLightType.Keyword)
return line.Replace(term, BuildForegroundTag(term, keywordColor));
if (type == HighLightType.CustomTerm)
return line.Replace(term, BuildBackgroundTag(term, statementColor));
return line;
}
public string ApplyHighlights(string code, string customTerm)
{
code = FormatCodeInXaml(code, true);
customTerm = FormatCodeInXaml(customTerm, false).Trim();
StringBuilder sb = new StringBuilder();
using (StreamReader sr = new StreamReader(new MemoryStream(Encoding.UTF8.GetBytes(code))))
{
while (!sr.EndOfStream)
{
string line = sr.ReadLine();
line = HighlightTerm(HighLightType.CustomTerm, customTerm, line);
foreach (string keyWord in KeyWords)
line = HighlightTerm(HighLightType.Keyword, keyWord, line);
foreach (string type in Types)
line = HighlightTerm(HighLightType.Type, type, line);
sb.AppendLine(line);
}
}
return sb.ToString();
}
}
Step 2. Add a Canvas XAML tag to your MainWindow.xaml
<Window x:Class="TestCodeVisualizer.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TestCodeVisualizer"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Canvas Name="canvas" />
</Window>
Step 3. In Your WPF Application add the following code: (make sure that test.txt is in the correct location) :
using System.Text;
using System.IO;
using System.Windows;
using System.Windows.Markup;
namespace TestCodeVisualizer
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
string testText = File.ReadAllText("Test.txt");
FrameworkElement fe = GenerateHighlightedTextBlock(testText, "Console.WriteLine");
this.canvas.Children.Add(fe);
}
private FrameworkElement GenerateHighlightedTextBlock(string code, string term)
{
CodeHighlighter ch = new CodeHighlighter();
string uc = "<UserControl xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>[CONTENT]</UserControl>";
string content = "<TextBlock>" + ch.ApplyHighlights(code, term) + "</TextBlock>";
uc = uc.Replace("[CONTENT]", content);
FrameworkElement fe = XamlReader.Load(new System.IO.MemoryStream(Encoding.UTF8.GetBytes(uc))) as FrameworkElement;
return fe;
}
}
}