【问题标题】:WPF TextBlock formatting bold from string propertyWPF TextBlock 从字符串属性格式化粗体
【发布时间】:2019-10-31 12:22:56
【问题描述】:

我已将 TextBlock 文本绑定到字符串属性。

Xaml 看起来像:

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

ModelView 类的属性如下所示:

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

我希望能够使用 Textblock 格式化功能,例如粗体。

例如:

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

在运行时文本块应该出现:

一些带有粗体字的字符串

这个问题的正确和更好的解决方案是什么?

正在搜索 MVVM 解决方案(无代码隐藏)。

【问题讨论】:

  • 也许使用 IValueConverter 并为每种格式返回不同的Run

标签: c# wpf data-binding text-formatting


【解决方案1】:

我会使用自定义附加属性来做到这一点。不是直接绑定到TextBlockText 属性,而是绑定到您自己的FormattedText 附加属性,然后使用PropertyChangedCallback 通过以编程方式设置TextBlockInlines 来处理格式化部分。

XAML 很简单,但这是因为大部分工作都是由附加属性完成的:

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

这是一个仅查找粗体和斜体格式标签的附加属性示例:

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);
        }
    }
}

【讨论】:

  • 我希望这个答案使用 XDocument 来解析 XML。当有人开始要求斜体和下划线时,这会让生活变得更轻松。
  • 我已修改答案以使用 XElement 解析并支持嵌套标签。希望这会更普遍有用。
  • 我想会的。谢谢!
  • 有没有办法在 Xaml 中或多或少地实现这个逻辑?我可以阻止属性的绑定。
  • Xaml 无法解析任意格式,这是上面大部分代码所做的。如果您想避免自定义解析,那么另一种选择是使用支持 RTF 的RichTextBox。但是,编写 RTF 文本很麻烦,通常不是您手动完成的。它也不能通过DependencyProperty 访问,因此要将RTF 与Xaml 一起使用,您需要继承RichTextBox 或使用上面的附加属性。您可以使用 WebBrowser 控件,但这通常是多余的,并且可能会添加您不想要的行为。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-16
  • 2010-09-12
  • 1970-01-01
  • 2015-07-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多