动态格式化的TextBlock

时间:2014-01-16 14:48:36

标签: c# wpf wpf-controls

我想动态格式化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;
    }
}

3 个答案:

答案 0 :(得分:2)

  

这不是试图回答这个问题......这是对问题评论中的问题的回应。

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

enter image description here

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

更新&gt;&gt;&gt;

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

尴尬,因为TextBlock没有ItemsSource属性,你可以绑定你的单词类集合...也许你可以使用{{ 1}}对于那部分...只是在这里大声思考......我现在要停止了。


更新&gt;&gt;&gt;

@KrzysztofBzowski,遗憾的是Converter属性 a TextBlock.Inlines,因此您将无法将数据绑定到它。然而,它让我思考,我做了另一次搜索,找到了关于Jevgeni Tsaikin的.NET实验室的Binding text containing tags to TextBlock inlines using attached property in Silverlight for Windows Phone 7文章。

这将涉及你宣布附属物和DependencyProperty,但看起来很有希望......试一试。并且不要担心它是针对Silverlight的......如果它在Silverlight中有效,那么它将在WPF中运行。

答案 1 :(得分:2)

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

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

它通过循环所需的高光来工作,从TextBlock.ContentStart TextPointer开始扫描每个短语的TextBlock。找到短语后,它可以构建一个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 interactiveity命名空间和您的行为命名空间:

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

答案 2 :(得分:0)

我有一个类似的用例,我需要使用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;

enter image description here