Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Highlight Search TextBlock

My goal is to create a custom TextBlock control that has a new dependency property, SearchText. This property will contain a regular expression. All occurrences of this regular expression in the text of the TextBlock will be highlighted using a custom style (another DP).

My current implementation involves clearing all of the Inline objects in the TextBlock's InlineCollection. I then fill the TextBlock with runs for unhighlighted text and runs for highlighted text with the style applied (this method does not support adding inlines directly to the TextBlock, instead TextBlock.TextProperty has to be used).

Works great, but sometimes I get a strange exception when trying to clear the Inlines: InvalidOperationException: "Cannot modify the logical children for this node at this time because a tree walk is in progress."

This problem seems to be related to this one. I am modifying the inlines in the TextChanged function, but I'm using a flag to avoid infinite recursive edits.

Any thoughts on how to architect this custom control? Is there a better way to do this? How do I get around this exception?

Thanks!

like image 630
Josh G Avatar asked Oct 06 '10 14:10

Josh G


People also ask

How do I highlight text in JavaScript search?

js code to highlight the text. There are many built-in functions in mark. js but we are using two functions for our requirement that is mark() and unmark() function respectively. Here mark() is used to highlight the search text and unmark() is used to remove highlighting the text that is highlighted before.

How do you highlight text in code?

The HTML <mark> tag is used to mark or highlight text that is of special interest or relevance in an HTML document. Browsers traditionally render the text found within the <mark> tag as text with a yellow background color.


2 Answers

In my implementation, I solved this by just adding another dependency property, called OriginalText. When it's modified, I updated both the Text property and update the highlighting. Here's the code:

  public class HighlightTextBlock : TextBlock
{
    public string HighlightedText
    {
        get { return (string)GetValue(HighlightedTextProperty); }
        set { SetValue(HighlightedTextProperty, value); }
    }

    public static readonly DependencyProperty HighlightedTextProperty =
        DependencyProperty.Register("HighlightedText", typeof(string), typeof(HighlightTextBlock), new UIPropertyMetadata(string.Empty, UpdateHighlightEffect));

    public static readonly DependencyProperty OriginalTextProperty = DependencyProperty.Register(
        "OriginalText", typeof(string), typeof(HighlightTextBlock), new PropertyMetadata(default(string), OnOriginalTextChanged));

    private static void OnOriginalTextChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
    {
        var block = ((HighlightTextBlock)obj);
        block.Text = block.OriginalText;
        block.UpdateHighlightEffect();
    }

    public string OriginalText
    {
        get { return (string)GetValue(OriginalTextProperty); }
        set { SetValue(OriginalTextProperty, value); }
    }

    private static void UpdateHighlightEffect(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        if (!(string.IsNullOrEmpty(e.NewValue as string) && string.IsNullOrEmpty(e.OldValue as string)))
            ((HighlightTextBlock)sender).UpdateHighlightEffect();
    }

    private void UpdateHighlightEffect()
    {
        if (string.IsNullOrEmpty(HighlightedText)) return;

        var allText = GetCompleteText();

        Inlines.Clear();

        var indexOfHighlightString = allText.IndexOf(HighlightedText, StringComparison.InvariantCultureIgnoreCase);

        if (indexOfHighlightString < 0)
        {
            Inlines.Add(allText);
        }
        else
        {
            Inlines.Add(allText.Substring(0, indexOfHighlightString));
            Inlines.Add(new Run()
                            {
                                Text = allText.Substring(indexOfHighlightString, HighlightedText.Length),
                                Background = Consts.SearchHighlightColor,
                            });
            Inlines.Add(allText.Substring(indexOfHighlightString + HighlightedText.Length));
        }

    }

    private string GetCompleteText()
    {
        var allText = Inlines.OfType<Run>().Aggregate(new StringBuilder(), (sb, run) => sb.Append(run.Text), sb => sb.ToString());
        return allText;
    }
}
like image 173
Omer Raviv Avatar answered Nov 15 '22 09:11

Omer Raviv


Still not sure if there's a better way to do this altogether, but I appear to have found a work around.

I was updating the inlines/runs in a function that was fired by the change notification for the TextProperty and the SearchTextProperty.

Now I'm firing the highlight/update code from a Dispatcher.BeginInvoke() call in the change notification with DispatcherPriority.Normal.

like image 41
Josh G Avatar answered Nov 15 '22 09:11

Josh G