1

The objective is to bold the word(s) of text in Textblock with matching input keyword.

For example: Stackoverflow is a very helpful, keep using Stackoverflow to sharpen your skills.

When the keyword is: Stackoverflow it should now display as

Stackoverflow is a very helpful, keep using Stackoverflow to sharpen your skills.

I tried to achieve the same objective using attached property. Below is the snap code of the same

public class HighLightKeyWord : DependencyObject
{

    //This word is used to specify the word to highlight
    public static readonly DependencyProperty BoldWordProperty = DependencyProperty.RegisterAttached("BoldWord", typeof(string), typeof(HighLightKeyWord),
        new PropertyMetadata(string.Empty, OnBindingTextChanged));

    public static string GetBoldWord(DependencyObject obj)
    {
        return (string)obj.GetValue(BoldWordProperty);
    }

    public static void SetBoldWord(DependencyObject obj, string value)
    {
        obj.SetValue(BoldWordProperty, value);
    }

    private static void OnBindingTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {

        var _Key = e.NewValue as string;
        var textblk = d as TextBlock;
        string SourceText = textblk.Text;
        SetBold(_Key, textblk, SourceText);

    }

    private static void SetBold(string key, TextBlock text, string SourceText)
    {
        text.Inlines.Clear();
        var words = SourceText.Split(' ');
        for (int i = 0; i < words.Length; i++)
        {
            var word = words[i];
            var inline = new Run() { Text = word + ' ' };
            if (String.Compare(word, key,  StringComparison.CurrentCultureIgnoreCase) == 0)
            {
                inline.FontWeight = FontWeights.Bold;
            }
            text.Inlines.Add(inline);
        }
    }

}

// Bind object in Main

        StackOverFlow stkovrflw = new StackOverFlow();
        stkovrflw.Text    = "Stackoverflow is a very helpful,keep using Stackoverflow to sharpen your skills.";
        stkovrflw.KeyWord = "Stackoverflow";
        this.DataContext = stkovrflw;

In Xaml I binded the value as

<TextBlock Text="{Binding Path=Text}"  loc:HighLightKeyWord.BoldWord="{Binding Path=KeyWord}" />

Above code is working fine, however when I directly sets the HighlightText property in the xaml instead through data binding, Text property of the Text block is getting empty in the OnBindingTextChanged method and this method is called only once when dependency property is set . I used this design based on Spellchecker concept so that other teamates can reuse my attached property in their projects. Can anyone suggest how to fix the problem?

Noam M
  • 3,156
  • 5
  • 26
  • 41

2 Answers2

1

Whether you want to modify a TextBlock FontWeight attribute or another display property, I've written the following static method and found it very useful. (Note: The method illustrates highlighting text by modifying the Foreground property. The same principle can be used for any other TextBlock display attribute.)

    static Brush DefaultHighlight = new SolidColorBrush(Colors.Red);

    public static void SetTextAndHighlightSubstring(this TextBlock targetTextBlock, string sourceText, string subString, Brush highlight = null)
    {
        if (targetTextBlock == null || String.IsNullOrEmpty(sourceText) || String.IsNullOrEmpty(subString))
            return;
        targetTextBlock.Text = "";
        var subStringPosition = sourceText.ToUpper().IndexOf(subString);
        if (subStringPosition == -1)
        {
            targetTextBlock.Inlines.Add(new Run { Text = sourceText });
            return;
        }
        var subStringLength = subString.Length;
        var header = sourceText.Substring(0, subStringPosition);
        subString = sourceText.Substring(subStringPosition, subStringLength);
        var trailerLength = sourceText.Length - (subStringPosition + subStringLength);
        var trailer = sourceText.Substring(subStringPosition + subStringLength, trailerLength);
        targetTextBlock.Inlines.Add(new Run { Text = header });
        targetTextBlock.Inlines.Add(new Run { Text = subString, Foreground = highlight ?? DefaultHighlight });
        targetTextBlock.Inlines.Add(new Run { Text = trailer });
    }

You can call this method using the TextBlock extension syntax like so:

TextBlockTitle.SetTextAndHighlightSubstring(_categoryItem.ItemText, _searchText);

In this example, the following display resulted:

enter image description here

Mark Jones
  • 2,024
  • 1
  • 19
  • 12
1

I recently ran into the same problem with the broken text binding. I realize this is an old question, but figured I'd share my finding anyway.

Basically Text property's data-binding is severed the moment Inlines.Clear is called. The way I got around this problem was by adding an internal text dependency property that replicated text's binding so subsequent text changes would continue to trigger highlighting.

Try adding something like this before clearing text.Inlines.

protected static string GetInternalText(DependencyObject obj)
{
    return (string)obj.GetValue(InternalTextProperty)
}

protected static void SetInternalText(DependencyObject obj, string value)
{
    obj.SetValue(InternalTextProperty, value);
}

// Using a DependencyProperty as the backing store for InternalText.  This enables animation, styling, binding, etc...
protected static readonly DependencyProperty InternalTextProperty =
        DependencyProperty.RegisterAttached("InternalText", typeof(string),
typeof(HighlightableTextBlock), new PropertyMetadata(string.Empty, OnInternalTextChanged));

private static void SetBold(string key, TextBlock text, string SourceText)
{
   //Add the following code to replicate text binding
   if (textblock.GetBindingExpression(HighlightableTextBlock.InternalTextProperty) == null)
   {
      var textBinding = text.GetBindingExpression(TextBlock.TextProperty);

      if (textBinding != null)
      {
          text.SetBinding(HighLightKeyWord.InternalTextProperty, textBinding.ParentBindingBase);
      }
   }

   ...
}

In InternalTextProperty's propertyChangeCallback you can have the same code as OnBindingTextChanged() or you could refactor them into another method.

For those who don't want to write their own code. I have created a lightweight nuget package that adds highlighting to regular TextBlock. You can leave most of your code intact and only have to add a few attached properties. By default the add-on supports highlighting, but you can also enable bold, italic and underline on the keyword.

https://www.nuget.org/packages/HighlightableTextBlock/

The source is here: https://github.com/kthsu/HighlightableTextBlock

Kevin H
  • 11
  • 3