-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
- Loading branch information
Showing
7 changed files
with
290 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
50 changes: 50 additions & 0 deletions
50
dev/DevWinUI.Gallery/Views/Pages/Features/RichTextFormatterPage.xaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
<?xml version="1.0" encoding="utf-8" ?> | ||
<Page x:Class="DevWinUIGallery.Views.RichTextFormatterPage" | ||
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:dev="using:DevWinUI" | ||
xmlns:local="using:DevWinUIGallery" | ||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" | ||
mc:Ignorable="d"> | ||
|
||
<ScrollViewer> | ||
<StackPanel Margin="10" | ||
dev:PanelAttach.ChildrenTransitions="Default" | ||
Spacing="10"> | ||
<local:ControlExample DocPage="helpers/richTextFormatter" | ||
HeaderText="TextBlock"> | ||
<local:ControlExample.CSharp> | ||
<x:String>RichTextFormatterHelper.FormatTextBlock(text, textBlock);</x:String> | ||
</local:ControlExample.CSharp> | ||
<local:ControlExample.Pane> | ||
<StackPanel Spacing="10"> | ||
<TextBox x:Name="Txt" | ||
Header="Input" | ||
PlaceholderText="Test of <b>bold</b> <i>italic</i> <u>underline</u><br>larger <font size='20'>font</font>||<font color='00c600'>Forecolor</font> <font bgcolor='00c600'> Background </font>||<font colors='cb3d00,004500'> Both combined </font>||regular" | ||
Text="Test of <b>bold</b> <i>italic</i> <u>underline</u><br>larger <font size='20'>font</font>||<font color='00c600'>Forecolor</font> <font bgcolor='00c600'> Background </font>||<font colors='cb3d00,004500'> Both combined </font>||regular" | ||
TextChanged="Txt_TextChanged" /> | ||
</StackPanel> | ||
</local:ControlExample.Pane> | ||
<TextBlock x:Name="txtBlock" /> | ||
</local:ControlExample> | ||
|
||
<local:ControlExample DocPage="helpers/richTextFormatter" | ||
HeaderText="RichTextBlock"> | ||
<local:ControlExample.CSharp> | ||
<x:String>RichTextFormatterHelper.FormatRichTextBlock(text, richTextBlock);</x:String> | ||
</local:ControlExample.CSharp> | ||
<local:ControlExample.Pane> | ||
<StackPanel Spacing="10"> | ||
<TextBox x:Name="Txt2" | ||
Header="Input" | ||
PlaceholderText="Test of <b>bold</b> <i>italic</i> <u>underline</u><br>larger <font size='20'>font</font>||<font color='00c600'>Forecolor</font> <font bgcolor='00c600'> Background </font>||<font colors='cb3d00,004500'> Both combined </font>||regular" | ||
Text="Test of <b>bold</b> <i>italic</i> <u>underline</u><br>larger <font size='20'>font</font>||<font color='00c600'>Forecolor</font> <font bgcolor='00c600'> Background </font>||<font colors='cb3d00,004500'> Both combined </font>||regular" | ||
TextChanged="Txt2_TextChanged" /> | ||
</StackPanel> | ||
</local:ControlExample.Pane> | ||
<RichTextBlock x:Name="txtRichBlock" /> | ||
</local:ControlExample> | ||
</StackPanel> | ||
</ScrollViewer> | ||
</Page> |
26 changes: 26 additions & 0 deletions
26
dev/DevWinUI.Gallery/Views/Pages/Features/RichTextFormatterPage.xaml.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
namespace DevWinUIGallery.Views; | ||
|
||
public sealed partial class RichTextFormatterPage : Page | ||
{ | ||
public RichTextFormatterPage() | ||
{ | ||
this.InitializeComponent(); | ||
Loaded += RichTextFormatterPage_Loaded; | ||
} | ||
|
||
private void RichTextFormatterPage_Loaded(object sender, RoutedEventArgs e) | ||
{ | ||
Txt_TextChanged(null, null); | ||
Txt2_TextChanged(null, null); | ||
} | ||
|
||
private void Txt_TextChanged(object sender, TextChangedEventArgs e) | ||
{ | ||
RichTextFormatterHelper.FormatTextBlock(Txt.Text, txtBlock); | ||
} | ||
|
||
private void Txt2_TextChanged(object sender, TextChangedEventArgs e) | ||
{ | ||
RichTextFormatterHelper.FormatRichTextBlock(Txt2.Text, txtRichBlock); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,194 @@ | ||
using Microsoft.UI.Text; | ||
using Microsoft.UI.Xaml.Documents; | ||
using System.Text.RegularExpressions; | ||
using Windows.UI.Text; | ||
|
||
namespace DevWinUI; | ||
public partial class RichTextFormatterHelper | ||
{ | ||
/// <summary> | ||
/// Formats a RichTextBlock using HTML-like formatting codes, supported tags: br, b, u, i, font size='size', and pipe characters which are treated like br. Also supports font color='hexcolor' and font bgcolor='hexcolor'. | ||
/// To combine both foreground and background colors, use font colors='hexforecolor,hexbackcolor' (colors separated by a comma). | ||
/// </summary> | ||
/// <param name="textWithHTMLFormatting"></param> | ||
/// <param name="richTextBlock"></param> | ||
public static void FormatRichTextBlock(string textWithHTMLFormatting, RichTextBlock richTextBlock) | ||
{ | ||
// Clearing existing content | ||
richTextBlock.Blocks.Clear(); | ||
|
||
if (textWithHTMLFormatting == null) | ||
return; | ||
|
||
// Replacing <br> with a unique placeholder that is unlikely to appear in the text | ||
string placeholder = "\uE000"; // Using a Private Use Area Unicode character as a placeholder | ||
string processedText = Regex.Replace(textWithHTMLFormatting, "<br>", placeholder, RegexOptions.IgnoreCase); // replace <br> with linebreak placeholder | ||
processedText = processedText.Replace("|", placeholder); // replace | with linebreak placeholder | ||
|
||
// Pattern to match various tags including foreground and background colors | ||
string pattern = $@"<b>(.*?)</b>|<u>(.*?)</u>|<i>(.*?)</i>|<font size='(.*?)'>(.*?)</font>|<font color='(.*?)'>(.*?)</font>|<font bgcolor='(.*?)'>(.*?)</font>|<font colors='(.*?)'>(.*?)</font>|{Regex.Escape(placeholder)}"; | ||
|
||
var paragraph = new Paragraph(); | ||
ProcessFormattingText(processedText, pattern, paragraph.Inlines); | ||
richTextBlock.Blocks.Add(paragraph); | ||
} | ||
|
||
/// <summary> | ||
/// Formats a TextBlock using HTML-like formatting codes, supported tags: br, b, u, i, font size='size', and pipe characters which are treated like br. Also supports font color='hexcolor', but TextBlocks do not support the background colors in this fashion, so use the RichTextBlock overload instead if backcolors are needed. | ||
/// </summary> | ||
/// <param name="textWithHTMLFormatting"></param> | ||
/// <param name="textBlock"></param> | ||
public static void FormatTextBlock(string textWithHTMLFormatting, TextBlock textBlock) | ||
{ | ||
// Clearing existing content | ||
textBlock.Inlines.Clear(); | ||
|
||
if (textWithHTMLFormatting == null) | ||
return; | ||
|
||
// Replacing <br> with a unique placeholder that is unlikely to appear in the text | ||
string placeholder = "\uE000"; // Using a Private Use Area Unicode character as a placeholder | ||
string processedText = Regex.Replace(textWithHTMLFormatting, "<br>", placeholder, RegexOptions.IgnoreCase); // replace <br> with linebreak placeholder | ||
processedText = processedText.Replace("|", placeholder); // replace | with linebreak placeholder | ||
|
||
// Pattern to match various tags including foreground colors | ||
string pattern = $@"<b>(.*?)</b>|<u>(.*?)</u>|<i>(.*?)</i>|<font size='(.*?)'>(.*?)</font>|<font color='(.*?)'>(.*?)</font>|{Regex.Escape(placeholder)}"; | ||
|
||
ProcessFormattingText(processedText, pattern, textBlock.Inlines); | ||
} | ||
|
||
/// <summary> | ||
/// Used by FormatTextBlock() to process the text and apply formatting to apply to the RichTextBlock or TextBlock | ||
/// </summary> | ||
/// <param name="text"></param> | ||
/// <param name="pattern"></param> | ||
/// <param name="inlines"></param> | ||
private static void ProcessFormattingText(string text, string pattern, InlineCollection inlines) | ||
{ | ||
int lastIndex = 0; | ||
|
||
foreach (Match match in Regex.Matches(text, pattern)) | ||
{ | ||
// Text before bold, underlined, italic, sized or line break | ||
if (match.Index > lastIndex) | ||
{ | ||
var runBeforeTag = new Run | ||
{ | ||
Text = text.Substring(lastIndex, match.Index - lastIndex) | ||
}; | ||
inlines.Add(runBeforeTag); | ||
} | ||
|
||
if (match.Value == "\uE000") // Handle line break | ||
{ | ||
inlines.Add(new LineBreak()); | ||
} | ||
else | ||
{ | ||
if (match.Groups[1].Success) // Handle bold text | ||
{ | ||
var span = new Span | ||
{ | ||
FontWeight = FontWeights.Bold | ||
}; | ||
ProcessFormattingText(match.Groups[1].Value, pattern, span.Inlines); | ||
inlines.Add(span); | ||
} | ||
else if (match.Groups[2].Success) // Handle underlined text | ||
{ | ||
var underline = new Underline(); | ||
ProcessFormattingText(match.Groups[2].Value, pattern, underline.Inlines); | ||
inlines.Add(underline); | ||
} | ||
else if (match.Groups[3].Success) // Handle italic text | ||
{ | ||
var span = new Span | ||
{ | ||
FontStyle = FontStyle.Italic | ||
}; | ||
ProcessFormattingText(match.Groups[3].Value, pattern, span.Inlines); | ||
inlines.Add(span); | ||
} | ||
else if (match.Groups[4].Success && match.Groups[5].Success) // Handle sized text | ||
{ | ||
var span = new Span | ||
{ | ||
FontSize = double.Parse(match.Groups[4].Value) | ||
}; | ||
ProcessFormattingText(match.Groups[5].Value, pattern, span.Inlines); | ||
inlines.Add(span); | ||
} | ||
else if (match.Groups[6].Success && match.Groups[7].Success) // Handle foreground color text | ||
{ | ||
var span = new Span | ||
{ | ||
Foreground = new SolidColorBrush(ColorHelper.GetColorFromHex(match.Groups[6].Value)) | ||
}; | ||
ProcessFormattingText(match.Groups[7].Value, pattern, span.Inlines); | ||
inlines.Add(span); | ||
} | ||
else if (match.Groups[8].Success && match.Groups[9].Success) // Handle background color text | ||
{ | ||
var span = new Span(); | ||
var border = new Border | ||
{ | ||
Background = new SolidColorBrush(ColorHelper.GetColorFromHex(match.Groups[8].Value)), | ||
Child = new TextBlock | ||
{ | ||
Text = match.Groups[9].Value, | ||
TextWrapping = TextWrapping.Wrap | ||
} | ||
}; | ||
|
||
var inlineUIContainer = new InlineUIContainer | ||
{ | ||
Child = border | ||
}; | ||
|
||
inlines.Add(inlineUIContainer); | ||
} | ||
else if (match.Groups[10].Success && match.Groups[11].Success) // Handle combined foreground and background color text | ||
{ | ||
var colors = match.Groups[10].Value.Split(','); | ||
var foregroundColor = ColorHelper.GetColorFromHex(colors[0]); | ||
var backgroundColor = ColorHelper.GetColorFromHex(colors[1]); | ||
|
||
var span = new Span | ||
{ | ||
Foreground = new SolidColorBrush(foregroundColor) | ||
}; | ||
|
||
var border = new Border | ||
{ | ||
Background = new SolidColorBrush(backgroundColor), | ||
Child = new TextBlock | ||
{ | ||
Text = match.Groups[11].Value, | ||
TextWrapping = TextWrapping.Wrap, | ||
Foreground = new SolidColorBrush(foregroundColor) | ||
} | ||
}; | ||
|
||
var inlineUIContainer = new InlineUIContainer | ||
{ | ||
Child = border | ||
}; | ||
|
||
inlines.Add(inlineUIContainer); | ||
} | ||
} | ||
|
||
lastIndex = match.Index + match.Length; | ||
} | ||
|
||
// Text after the last tag, if any | ||
if (lastIndex < text.Length) | ||
{ | ||
var runAfterLastTag = new Run | ||
{ | ||
Text = text.Substring(lastIndex) | ||
}; | ||
inlines.Add(runAfterLastTag); | ||
} | ||
} | ||
} |