Textプロパティに所定の書式に従った文字列突っ込んだらいい感じに色つけて表示してくれるTextBlockみたいなのが欲しくなることってないでしょうか。
そんな時は、TextBlockを1枚ラップしたカスタムコントロールかユーザーコントロールを作ると捗ります。今回は、実装が簡単なユーザーコントロールを例に作ってみたいと思います。
ColoredTextBlockという名前でユーザーコントロールを作って以下のようにTextBlockを置きます。
<UserControl x:Class="ColoredTextBlock.ColoredTextBlock" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ColoredTextBlock" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400"> <Grid> <TextBlock x:Name="TextBlock" /> </Grid> </UserControl>
そして、このユーザーコントロールにText依存関係プロパティを作ります。Textが変更されたときのコールバックでTextをパースしてTextBlockのInlinesを組み立てます。
using System.Reflection; using System.Xml.Linq; using Windows.UI; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Documents; using Windows.UI.Xaml.Media; namespace ColoredTextBlock { public sealed partial class ColoredTextBlock : UserControl { public static readonly DependencyProperty TextProperty = DependencyProperty.Register( nameof(Text), typeof(string), typeof(ColoredTextBlock), new PropertyMetadata(null, (s, e) => { ((ColoredTextBlock)s).Parse(); })); public string Text { get { return (string)GetValue(TextProperty); } set { SetValue(TextProperty, value); } } private void Parse() { // ここで文字列をパースしてInlinesを組み立てる } public ColoredTextBlock() { this.InitializeComponent(); } } }
今回はXML形式でFontタグにColor属性で色を指定できるみたいなのをイメージして以下のようなパース処理を実装しました。
this.TextBlock.Inlines.Clear(); try { var doc = XDocument.Parse(this.Text); foreach (var node in doc.Root.Nodes()) { if (node.NodeType == System.Xml.XmlNodeType.Text) { var textNode = (XText)node; this.TextBlock.Inlines.Add(new Run { Text = textNode.Value }); } else if (node.NodeType == System.Xml.XmlNodeType.Element) { var elm = (XElement)node; if (elm.Name != "Font") { continue; } var color = elm.Attribute("Color")?.Value ?? "Black"; var text = elm.Value; var property = (Color)(typeof(Colors).GetTypeInfo().GetDeclaredProperty(color)?.GetValue(null) ?? Colors.Black); this.TextBlock.Inlines.Add(new Run { Text = text, Foreground = new SolidColorBrush(property) }); } } } catch { }
以下のようにTextBoxの入力とバインドして使ってみます。
<Page x:Class="ColoredTextBlock.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ColoredTextBlock" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <TextBox x:Name="TextBox" Text="<Doc><Font Color='Blue'>Hello</Font> <Font Color='Red'>World</Font></Doc>" /> <local:ColoredTextBlock Text="{x:Bind TextBox.Text, Mode=OneWay}" /> </StackPanel> </Page>
実行すると以下のようになります。
ソースコード
ソースコードの全体はGitHubに上げています。