【问题标题】:Dynamically formatted TextBlock动态格式化的文本块
【发布时间】:2018-05-19 00:55:38
【问题描述】:

我想动态格式化 TextBlock 中的特定单词,因为它绑定到我的对象。我正在考虑使用 Converter 但使用以下解决方案仅将标签直接添加到文本中(而不是显示它的格式)。

public class TextBlockFormatter : IValueConverter {
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
        string regexp = @"\p{L}+#\d{4}";

        if (value != null) {
            return Regex.Replace(value as string, regexp, m => string.Format("<Bold>{0}</Bold>", m.Value));
        } 

        return null;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
        return null;
    }
}

【问题讨论】:

  • 您要么需要使用复杂的RichTextBox class,要么创建一些自定义方式在TextBlock 中生成单独的Run elements
  • @Sheridan 很确定 RichTextBox 不支持绑定,如果有转换器,我怀疑存在绑定。
  • 如果您不使用转换器,是否可以在 TextBlock 中格式化单个单词?
  • 我用过这个,它奏效了。而且速度很快。由于 InLines 是只读的,因此需要欺骗它。 stackoverflow.com/questions/3728584/…

标签: c# wpf wpf-controls


【解决方案1】:

这不是试图回答这个问题...这是对问题 cmets 中问题的回答。

是的@Blam,你可以格式化单个单词,甚至是TextBlock中的字符...你需要使用Run(或者你可以Run 替换为TextBlock) 类。无论哪种方式,您还可以将数据绑定到其中任何一个上的 Text 属性:

<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center">
    <Run Text="This" FontWeight="Bold" Foreground="Red" />
    <Run Text="text" FontSize="18" />
    <Run Text="is" FontStyle="Italic" />
    <Run Text="in" FontWeight="SemiBold" Background="LightGreen" />
    <Run Text="one" FontFamily="Candara" FontSize="20" />
    <Run Text="TextBlock" FontWeight="Bold" Foreground="Blue" />
</TextBlock>

更新 >>>

现在关于这个问题,我想可以在DataTemplate 中使用这些Run 元素并让它们从数据中生成......在这种情况下,数据必须是具有(显然) 一个Text 属性,还包括可用于数据绑定到Run 样式属性的格式化属性。

很尴尬,因为TextBlock 没有ItemsSource 属性,您可以将您的单词类集合绑定到...也许您可以使用Converter 来处理该部分...只是在这里大声思考...我现在要停下来。


更新>>>

@KrzysztofBzowski,不幸的是,TextBlock.Inlines 属性不是DependencyProperty,因此您将无法对其进行数据绑定。然而,这让我开始思考,我又进行了一次搜索,发现了关于 Jevgeni Tsaikin 的 .NET 实验室的 Binding text containing tags to TextBlock inlines using attached property in Silverlight for Windows Phone 7 文章。

这将涉及您声明一个附加属性和一个Converter,但它看起来很有希望......试一试。不用担心它适用于 Silverlight……如果它适用于 Silverlight,那么它也适用于 WPF。

【讨论】:

  • 谢谢,我不知道你可以直接在 TextBlock 中使用 Run。我只在 FlowDocument 中知道它们。
  • 是的...您甚至可以在其他 TextBlock 元素中使用 TextBlock 元素,例如。将此处的Run 元素替换为TextBlocks。
  • 我需要格式化和复制粘贴(但只读)。似乎 TextBox 不支持 InLines,因此不支持突出显示。有什么想法吗。现在我使用 FlowDocumentScrollViewer 但它很慢。
  • 同时我发现:stackoverflow.com/questions/2891736/… 但我更喜欢与 MVVM 方法一致的东西。
【解决方案2】:

我最近不得不通过为 TextBlocks 编写混合行为来解决这个问题。

它可以在 XAML 中声明,其中包含突出显示元素的列表,您可以在其中指定要突出显示的文本、您希望该文本的颜色及其字体粗细(可以根据需要轻松添加更多格式属性)。

它的工作原理是循环遍历所需的亮点,扫描 TextBlock 以查找从 TextBlock.ContentStart TextPointer 开始的每个短语。一旦找到该短语,它就可以构建一个 TextRange,它可以应用格式选项。

如果 TextBlock Text 属性也是数据绑定的,它应该可以工作,因为我附加到绑定目标更新事件。

XAML 中的行为代码和示例见下文

public class TextBlockHighlightBehaviour : Behavior<TextBlock>
{
    private EventHandler<DataTransferEventArgs> targetUpdatedHandler;
    public List<Highlight> Highlights { get; set; }

    public TextBlockHighlightBehaviour()
    {
        this.Highlights = new List<Highlight>();
    }

    #region Behaviour Overrides

    protected override void OnAttached()
    {
        base.OnAttached();
        targetUpdatedHandler = new EventHandler<DataTransferEventArgs>(TextBlockBindingUpdated);
        Binding.AddTargetUpdatedHandler(this.AssociatedObject, targetUpdatedHandler);

        // Run the initial behaviour logic
        HighlightTextBlock(this.AssociatedObject);
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        Binding.RemoveTargetUpdatedHandler(this.AssociatedObject, targetUpdatedHandler);
    }

    #endregion

    #region Private Methods

    private void TextBlockBindingUpdated(object sender, DataTransferEventArgs e)
    {
        var textBlock = e.TargetObject as TextBlock;
        if (textBlock == null)
            return;

        if(e.Property.Name == "Text")
            HighlightTextBlock(textBlock);
    }

    private void HighlightTextBlock(TextBlock textBlock)
    {
        foreach (var highlight in this.Highlights)
        {
            foreach (var range in FindAllPhrases(textBlock, highlight.Text))
            {
                if (highlight.Foreground != null)
                    range.ApplyPropertyValue(TextElement.ForegroundProperty, highlight.Foreground);

                if(highlight.FontWeight != null)
                    range.ApplyPropertyValue(TextElement.FontWeightProperty, highlight.FontWeight);
            }
        }
    }

    private List<TextRange> FindAllPhrases(TextBlock textBlock, string phrase)
    {
        var result = new List<TextRange>();
        var position = textBlock.ContentStart;

        while (position != null)
        {
            var range = FindPhrase(position, phrase);
            if (range != null)
            {
                result.Add(range);
                position = range.End;
            }
            else
                position = null;
        }

        return result;
    }

    // This method will search for a specified phrase (string) starting at a specified position.
    private TextRange FindPhrase(TextPointer position, string phrase)
    {
        while (position != null)
        {
            if (position.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.Text)
            {
                string textRun = position.GetTextInRun(LogicalDirection.Forward);

                // Find the starting index of any substring that matches "phrase".
                int indexInRun = textRun.IndexOf(phrase);
                if (indexInRun >= 0)
                {
                    TextPointer start = position.GetPositionAtOffset(indexInRun);
                    TextPointer end = start.GetPositionAtOffset(phrase.Length);
                    return new TextRange(start, end);
                }
            }

            position = position.GetNextContextPosition(LogicalDirection.Forward);
        }

        // position will be null if "phrase" is not found.
        return null;
    }

    #endregion
}

public class Highlight
{
    public string Text { get; set; }
    public Brush Foreground { get; set; }
    public FontWeight FontWeight { get; set; }
}

XAML 中的示例用法:

<TextBlock Text="Here is some text">
   <i:Interaction.Behaviors>
      <behaviours:TextBlockHighlightBehaviour>
         <behaviours:TextBlockHighlightBehaviour.Highlights>
            <behaviours:Highlight Text="some" Foreground="{StaticResource GreenBrush}" FontWeight="Bold" />
            </behaviours:TextBlockHighlightBehaviour.Highlights>
         </behaviours:TextBlockHighlightBehaviour>
   </i:Interaction.Behaviors>
</TextBlock>

您需要导入 Blend 交互命名空间和行为的命名空间:

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:behaviours="clr-namespace:YourProject.Behviours"

【讨论】:

  • 为了进一步解释,通过将格式应用于代码中的 TextRange 对象,它实际上将一系列 Runs 添加到 TextBlock.Inlines 集合中,很酷吧?
【解决方案3】:

我有一个类似的用例,我需要用 RichTextBox 构建一个文本编辑器。但是,所有文本格式更改:字体、颜色、斜体、粗体必须动态反映在 TextBlock 上。我发现很少有文章将我指向 Textblock.inlines.Add(),这似乎很有帮助,但一次只允许进行一项更改或附加到现有文本。

但是,Textblock.inlines.ElementAt(现有文本的索引要格式化) 可用于将所需的文本格式应用于位于该索引处的文本。以下是我解决此问题的伪方法。我希望这会有所帮助:

For RichTextBox:    
selectedText.ApplyPropertyValue(TextElement.FontFamilyProperty, cbFontFamily.SelectedValue.ToString());

但是,为了使 Textblock 格式正常工作,我必须使用 Run run = new Run() 概念,它允许并使用:

Foreground = new SolidColorBrush((Color)ColorConverter.ConvertFromString(SelectedColorText))
FontFamily = new FontFamily(cbFontFamily.SelectedValue.ToString())
FontStyle = FontStyles.Italic
TextDecorations = TextDecorations.Underline
TextDecorations = TextDecorations.Strikethrough
FontWeight = (FontWeight)new FontWeightConverter().ConvertFromString(cbFontWeightBox.SelectedItem.ToString())

此外,我创建了一个包含各种字段和构造函数的类。我还创建了一个基于自定义的类字典来捕获在 RichTextbbox 中所做的所有更改。最后,我通过 forloop 将所有捕获的信息应用到字典中。

TextBlock.Inlines.ElementAt(mItemIndex).Foreground = new SolidColorBrush((Color)ColorConverter.ConvertFromString(dictionaryItem.Value._applyColor.ToString()));

TextBlock.Inlines.ElementAt(mItemIndex).FontFamily = new FontFamily(ftItem.Value._applyFontFamily.ToString());

TextBlock.Inlines.ElementAt(mItemIndex).FontStyle = FontStyles.Italic;

TextBlock.Inlines.ElementAt(mItemIndex).TextDecorations = TextDecorations.Underline;

TextBlock.Inlines.ElementAt(mItemIndex).TextDecorations = TextDecorations.Strikethrough;

【讨论】:

    猜你喜欢
    • 2015-11-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多