突出显示搜索文本块

发布于 2024-09-26 04:45:53 字数 630 浏览 8 评论 0原文

我的目标是创建一个具有新依赖属性 SearchText 的自定义 TextBlock 控件。该属性将包含一个正则表达式。 TextBlock 文本中出现的所有正则表达式都将使用自定义样式(另一个 DP)突出显示。

我当前的实现涉及清除 TextBlock 的 InlineCollection 中的所有 Inline 对象。然后,我使用未突出显示的文本的运行填充 TextBlock,并使用应用的样式运行突出显示的文本(此方法不支持直接向 TextBlock 添加内联,而是必须使用 TextBlock.TextProperty)。

效果很好,但有时在尝试清除内联时会遇到奇怪的异常:InvalidOperationException:“此时无法修改此节点的逻辑子节点,因为正在进行树遍历。”

这个问题似乎与这个有关。我正在修改 TextChanged 函数中的内联,但我使用标志来避免无限递归编辑。

关于如何构建这个自定义控件有什么想法吗?有更好的方法吗?我该如何解决这个异常?

谢谢!

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!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

被你宠の有点坏 2024-10-03 04:45:53

在我的实现中,我通过添加另一个名为 OriginalText 的依赖属性来解决这个问题。修改后,我更新了 Text 属性并更新了突出显示。这是代码:

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

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;
    }
}
远昼 2024-10-03 04:45:53

仍然不确定是否有更好的方法来完成此操作,但我似乎找到了解决方法。

我正在更新由 TextProperty 和 SearchTextProperty 的更改通知触发的函数中的内联/运行。

现在,我使用 DispatcherPriority.Normal 从更改通知中的 Dispatcher.BeginInvoke() 调用中触发突出显示/更新代码。

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.

吹泡泡o 2024-10-03 04:45:53

如果有人想要一个如何执行此操作的示例,我找到了这个

In case anyone wants an example of how to do this, I found this

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文