WPF TextBlock formatting bold from string property

让人想犯罪 __ 提交于 2021-01-28 11:44:22

问题


I have binded TextBlock text to a string property.

Xaml look like:

<TextBlock Text="{Binding Message}" TextWrapping="Wrap"/>

Property on the ModelView class look like:

private string message;
public string Message
{
    get { return message; }
    set
    {
        message = value;
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Message)));
    }
}

I want to be able to use Textblock formatting abilities such as bold.

for example:

Message = "Some string with <Bold>bold</Bold> words";

On run time Textblock should present:

Some string with bold words

What is the right and better solution for this problem?

Searching for MVVM solution (without code behind).


回答1:


I would do this using an custom attached property. Instead of binding directly to the TextBlock's Text property, you would bind to your own FormattedText attached property and then use a PropertyChangedCallback to handle the formatting part by programmatically setting the TextBlock's Inlines.

The XAML is simple enough but that's because most of the work is done by the attached property:

<TextBlock local:TextBlockFormatter.FormattedText="{Binding FormattedText, Mode=OneWay}" />

This is a an example of an attached property which only looks for bold and italic formatting tags:

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Xml.Linq;

...

public class TextBlockFormatter
{
    public static readonly DependencyProperty FormattedTextProperty = DependencyProperty.RegisterAttached(
        "FormattedText",
        typeof(string),
        typeof(TextBlockFormatter),
        new FrameworkPropertyMetadata(null, OnFormattedTextChanged));

    public static void SetFormattedText(UIElement element, string value)
    {
        element.SetValue(FormattedTextProperty, value);
    }

    public static string GetFormattedText(UIElement element)
    {
        return (string)element.GetValue(FormattedTextProperty);
    }

    private static void OnFormattedTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var textblock = (TextBlock)d;
        var formatted = (string)e.NewValue;
        if (string.IsNullOrEmpty(formatted))
            textblock.Text = "";
        else
        {
            textblock.Inlines.Clear();
            try
            {
                var nodeStack = new Stack<StyleStackNode>();
                var root = XElement.Parse("<root>" + formatted + "</root>");
                nodeStack.Push(new StyleStackNode(root.FirstNode));
                while (nodeStack.Count > 0)
                {
                    var format = nodeStack.Pop();
                    if (format.Node.NextNode != null)
                        nodeStack.Push(new StyleStackNode(format.Node.NextNode, copyFormats: format.Formatters));
                    if (format.Node is XElement tag && tag.FirstNode != null)
                    {
                        var adding = new StyleStackNode(tag.FirstNode, copyFormats: format.Formatters);
                        if (0 == string.Compare(tag.Name.LocalName, "bold", true))
                            adding.Formatters.Add(run => run.FontWeight = FontWeights.Bold);
                        else if (0 == string.Compare(tag.Name.LocalName, "italic", true))
                            adding.Formatters.Add(run => run.FontStyle = FontStyles.Italic);
                        nodeStack.Push(adding);
                    }
                    else if (format.Node is XText textNode)
                    {
                        var run = new Run();
                        foreach (var formatter in format.Formatters)
                            formatter(run);
                        run.Text = textNode.Value;
                        textblock.Inlines.Add(run);
                    }
                }
            }
            catch
            {
                textblock.Text = formatted;
            }
        }
    }

    class StyleStackNode
    {
        public XNode Node;
        public List<Action<Run>> Formatters = new List<Action<Run>>();
        public StyleStackNode(XNode node, IEnumerable<Action<Run>> copyFormats = null)
        {
            Node = node;
            if (copyFormats != null)
                Formatters.AddRange(copyFormats);
        }
    }
}


来源:https://stackoverflow.com/questions/58643293/wpf-textblock-formatting-bold-from-string-property

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!