WPF Generate TextBlock Inlines WPF Generate TextBlock Inlines wpf wpf

WPF Generate TextBlock Inlines


An old question, but I find accepted answer an absolute overkill. You don't need to parse the formatted text at all! Just wrap it up in Span element and you are done.

public class Attached{    public static readonly DependencyProperty FormattedTextProperty = DependencyProperty.RegisterAttached(        "FormattedText",         typeof(string),         typeof(Attached),         new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.AffectsMeasure, FormattedTextPropertyChanged));    public static void SetFormattedText(DependencyObject textBlock, string value)    {        textBlock.SetValue(FormattedTextProperty, value);    }    public static string GetFormattedText(DependencyObject textBlock)    {        return (string)textBlock.GetValue(FormattedTextProperty);    }    private static void FormattedTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)    {        var textBlock = d as TextBlock;        if (textBlock == null)        {            return;        }        var formattedText = (string)e.NewValue ?? string.Empty;        formattedText = string.Format("<Span xml:space=\"preserve\" xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">{0}</Span>", formattedText);        textBlock.Inlines.Clear();        using (var xmlReader = XmlReader.Create(new StringReader(formattedText)))        {            var result = (Span)XamlReader.Load(xmlReader);            textBlock.Inlines.Add(result);        }    }}

Now you can use the FormattedText attached property either in your code:

string inlineExpression = "<Run Style=\"Theme.GrayText\">Once I saw a little bird, go hop, hop, hop.</Run>";Attached.SetFormattedText(myTextBlock1, inlineExpression);

More importantly, straight from the XAML:

<TextBlock ns:Attached.FormattedText="{Binding Content}" />

Where ns is the namespace you defined the Attached class in.


Recently i came across the same problem. So i decided to implement an attached property for TextBlock which takes a value of string type and then populates the Inlines collection on the fly. You can simply set the property value to something like this:

string inlineExpression = "Once i saw a little <bold>bird</bold>, <span>go <bold>hop, hop, hop</bold></span>.";InlineExpression.SetInlineExpression(myTextBlock1, inlineExpression);

The styles are also supported:

string inlineExpression = "<run style="Theme.GrayText">Once i saw a little bird, go hop, hop, hop.</run>";InlineExpression.SetInlineExpression(myTextBlock1, inlineExpression);

You can also use this attached property in XAML in a standard way.

Here is the code of the class which exposes this property:

public class InlineExpression{    public static readonly DependencyProperty InlineExpressionProperty = DependencyProperty.RegisterAttached(        "InlineExpression", typeof(string), typeof(TextBlock), new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.AffectsMeasure));    public static void SetInlineExpression(TextBlock textBlock, string value)    {        textBlock.SetValue(InlineExpressionProperty, value);        textBlock.Inlines.Clear();        if (string.IsNullOrEmpty(value))            return;        var descriptions = GetInlineDescriptions(value);        if (descriptions.Length == 0)            return;        var inlines = GetInlines(textBlock, descriptions);        if (inlines.Length == 0)            return;        textBlock.Inlines.AddRange(inlines);    }    public static string GetInlineExpression(TextBlock textBlock)    {        return (string)textBlock.GetValue(InlineExpressionProperty);    }    enum InlineType    {        Run,        LineBreak,        Span,        Bold,        Italic,        Hyperlink,        Underline    }    class InlineDescription    {        public InlineType Type { get; set; }        public string Text { get; set; }        public InlineDescription[] Inlines { get; set; }        public string StyleName { get; set; }    }    private static Inline[] GetInlines(FrameworkElement element, IEnumerable<InlineDescription> inlineDescriptions)    {        var inlines = new List<Inline>();        foreach (var description in inlineDescriptions)        {            var inline = GetInline(element, description);            if (inline != null)                inlines.Add(inline);        }        return inlines.ToArray();    }    private static Inline GetInline(FrameworkElement element, InlineDescription description)    {        Style style = null;        if (!string.IsNullOrEmpty(description.StyleName))        {            style = element.FindResource(description.StyleName) as Style;            if (style == null)                throw new InvalidOperationException("The style '" + description.StyleName + "' cannot be found");        }        Inline inline = null;        switch (description.Type)        {            case InlineType.Run:                var run = new Run(description.Text);                inline = run;                break;            case InlineType.LineBreak:                var lineBreak = new LineBreak();                inline = lineBreak;                break;            case InlineType.Span:                var span = new Span();                inline = span;                break;            case InlineType.Bold:                var bold = new Bold();                inline = bold;                break;            case InlineType.Italic:                var italic = new Italic();                inline = italic;                break;            case InlineType.Hyperlink:                var hyperlink = new Hyperlink();                inline = hyperlink;                break;            case InlineType.Underline:                var underline = new Underline();                inline = underline;                break;        }        if (inline != null)        {            var span = inline as Span;            if (span != null)            {                var childInlines = new List<Inline>();                foreach (var inlineDescription in description.Inlines)                {                    var childInline = GetInline(element, inlineDescription);                    if (childInline == null)                        continue;                    childInlines.Add(childInline);                }                span.Inlines.AddRange(childInlines);            }            if (style != null)                inline.Style = style;        }        return inline;    }    private static InlineDescription[] GetInlineDescriptions(string inlineExpression)    {        if (inlineExpression == null)            return new InlineDescription[0];        inlineExpression = inlineExpression.Trim();        if (inlineExpression.Length == 0)            return new InlineDescription[0];        inlineExpression = inlineExpression.Insert(0, @"<root>");        inlineExpression = inlineExpression.Insert(inlineExpression.Length, @"</root>");        var xmlTextReader = new XmlTextReader(new StringReader(inlineExpression));        var xmlDocument = new XmlDocument();        xmlDocument.Load(xmlTextReader);        var rootElement = xmlDocument.DocumentElement;        if (rootElement == null)            return new InlineDescription[0];        var inlineDescriptions = new List<InlineDescription>();        foreach (XmlNode childNode in rootElement.ChildNodes)        {            var description = GetInlineDescription(childNode);            if (description == null)                continue;            inlineDescriptions.Add(description);        }        return inlineDescriptions.ToArray();    }    private static InlineDescription GetInlineDescription(XmlNode node)    {        var element = node as XmlElement;        if (element != null)            return GetInlineDescription(element);        var text = node as XmlText;        if (text != null)            return GetInlineDescription(text);        return null;    }    private static InlineDescription GetInlineDescription(XmlElement element)    {        InlineType type;        var elementName = element.Name.ToLower();        switch (elementName)        {            case "run":                type = InlineType.Run;                break;            case "linebreak":                type = InlineType.LineBreak;                break;            case "span":                type = InlineType.Span;                break;            case "bold":                type = InlineType.Bold;                break;            case "italic":                type = InlineType.Italic;                break;            case "hyperlink":                type = InlineType.Hyperlink;                break;            case "underline":                type = InlineType.Underline;                break;            default:                return null;        }        string styleName = null;        var attribute = element.GetAttributeNode("style");        if (attribute != null)            styleName = attribute.Value;        string text = null;        var childDescriptions = new List<InlineDescription>();        if (type == InlineType.Run || type == InlineType.LineBreak)        {            text = element.InnerText;        }        else        {            foreach (XmlNode childNode in element.ChildNodes)            {                var childDescription = GetInlineDescription(childNode);                if (childDescription == null)                    continue;                childDescriptions.Add(childDescription);            }        }        var inlineDescription = new InlineDescription                                        {                                            Type = type,                                            StyleName = styleName,                                            Text = text,                                            Inlines = childDescriptions.ToArray()                                        };        return inlineDescription;    }    private static InlineDescription GetInlineDescription(XmlText text)    {        var value = text.Value;        if (string.IsNullOrEmpty(value))            return null;        var inlineDescription = new InlineDescription                                        {                                            Type = InlineType.Run,                                            Text = value                                        };        return inlineDescription;    }}


In XAML you could do something like this:

<GridViewColumn>    <GridViewColumn.CellTemplate>        <DataTemplate>            <TextBlock>                <Hyperlink NavigateUri="{Binding AUri}">                    <Run Text="{Binding A}"/>                </Hyperlink>                <Run Text=" is doing "/>                <Hyperlink NavigateUri="{Binding BUri}">                    <Run Text="{Binding B}"/>                </Hyperlink>            </TextBlock>        </DataTemplate>    </GridViewColumn.CellTemplate></GridViewColumn>

In code behind the same thing can be done but i would not recommend it since it involves using FrameworkElementFactories.