【问题标题】:WPF Generate TextBlock InlinesWPF 生成 TextBlock 内联
【发布时间】:2011-04-07 14:41:15
【问题描述】:

我有一个GridView,在其中一个GridViewColumns 中我想生成这样的文本:

textBlock.Text = string.Format("{0} is doing {1} .......", a, b);

ab (视图中项目的属性) 不应仅表示为纯文本,而应表示为 Hyperlink 例如。
(另外:格式文本应该取决于项目的类型)

我怎样才能以这种方式生成TextBlocks 文本? (用于本地化)

问题更多:我应该自己写一些东西还是框架提供了一种简单的方法?

【问题讨论】:

    标签: wpf binding string-formatting


    【解决方案1】:

    在 XAML 中你可以这样做:

    <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>
    

    可以在代码后面做同样的事情,但我不推荐它,因为它涉及使用FrameworkElementFactories

    【讨论】:

    • 是的,那将是最坏的情况。但是文本比“正在做”要长,我会有很多小部分要翻译=/
    【解决方案2】:

    一个老问题,但我发现接受的答案绝对是矫枉过正。您根本不需要解析格式化的文本!只需将其包裹在 Span 元素中即可完成。

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

    现在您可以在代码中使用FormattedText 附加属性:

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

    更重要的是,直接来自 XAML:

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

    其中ns 是您在其中定义Attached 类的命名空间。

    【讨论】:

    • 好主意。但是我认为没有机会在您的解决方案中使用样式:XamlReader 会抛出异常,因为样式是在其他地方定义的。此外,您必须预处理/解析文本以使“运行”变为“运行”等等。无论如何,我认为这对于许多情况都是很好的解决方案。
    • @Evgeni:只要您通过DynamicResource 引用样式,它使用样式。更重要的是,您不需要解析任何内容(我将“run”更改为“Run”,这是一个错字),与笨拙的公认答案相比,这就是它的美妙之处。
    • 第一个答案的美妙之处在于它早先给出并且可能会解决问题))我认为最重要的是两种解决方案最终都提供了一个附加属性。毫无疑问,您的解决方案要优雅得多。
    • 这是一个很好的解决方案 (+1),但我必须对 RegisterAttached 进行一些更正才能将其设置为在 XAML 中进行绑定:(1) typof(TextBlock) -> @987654331 @ 和 (2) 在 new FrameworkPropertyMetadata 中添加 FormattedPropertyChanged 作为第三个参数。此外,要在 XAML 中使用:&lt;TextBlock ns:Formatter="{Binding Content}" /&gt;。 @gwiazdorrr,如果您可以编辑答案以防人们看不到我的评论,那就太好了。 (我会自己编辑,但我不确定我的任何更改是否会干扰您的代码/非 XAML 解决方案。)
    • 感谢您的关注!事实是我使用自定义依赖属性工厂,这是快速而肮脏的翻译回原版 WPF。
    【解决方案3】:

    最近我遇到了同样的问题。所以我决定为TextBlock 实现一个附加属性,它的值是string 类型,然后动态填充Inlines 集合。您可以简单地将属性值设置为如下所示:

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

    也支持样式:

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

    您还可以在 XAML 中以标准方式使用此附加属性。

    这里是暴露这个属性的类的代码:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-20
      • 2011-04-27
      • 1970-01-01
      相关资源
      最近更新 更多