WPF TextBlock根据搜索条件突出显示某些部分

Dan*_*rik 14 wpf highlighting textblock

我有TextBlock动态添加Inlines(基本上是一堆斜体或粗体的Run对象).

在我的应用程序中,我有搜索功能.

我希望能够突出显示正在搜索的TextBlock文本.

通过突出显示我的意思是更改TextBlock文本颜色的某些部分(请记住,它可能一次突出显示几个不同的Run对象).

我试过这个例子http://blogs.microsoft.co.il/blogs/tamir/archive/2008/05/12/search-and-highlight-any-text-on-wpf-rendered-page.aspx

但它接缝非常不稳定:(

有没有简单的方法来解决这个问题?

dth*_*her 17

此问题类似于如何在突出显示的查询字词的WPF项目控件中显示搜索结果

在回答这个问题时,我提出了一种使用IValueConverter的方法.转换器获取文本片段,将其格式化为有效的XAML标记,并使用XamlReader将标记实例化为框架对象.

完整的解释相当长,所以我把它发布到我的博客:突出显示WPF TextBlock中的查询条款


Syn*_*les 10

我接受了dthrasers的回答,并且需要一个XML解析器.他很好地解释了他博客中的每一个部分,但是这并没有要求我添加任何额外的库,这就是我如何做到的.

第一步,制作转换器类:

class StringToXamlConverter : IValueConverter
    {

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            string input = value as string;
            if (input != null)
            {
                var textBlock = new TextBlock();
                textBlock.TextWrapping = TextWrapping.Wrap;
                string escapedXml = SecurityElement.Escape(input);

                while (escapedXml.IndexOf("|~S~|") != -1) {
                //up to |~S~| is normal
                textBlock.Inlines.Add(new Run(escapedXml.Substring(0, escapedXml.IndexOf("|~S~|"))));
                //between |~S~| and |~E~| is highlighted
                textBlock.Inlines.Add(new Run(escapedXml.Substring(escapedXml.IndexOf("|~S~|") + 5,
                                          escapedXml.IndexOf("|~E~|") - (escapedXml.IndexOf("|~S~|") + 5))) 
                                          { FontWeight = FontWeights.Bold, Background= Brushes.Yellow });
                //the rest of the string (after the |~E~|)
                escapedXml = escapedXml.Substring(escapedXml.IndexOf("|~E~|") + 5);
                }

                if (escapedXml.Length > 0)
                {
                    textBlock.Inlines.Add(new Run(escapedXml));                      
                }
                return textBlock;
            }

            return null;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException("This converter cannot be used in two-way binding.");
        }

    }
Run Code Online (Sandbox Code Playgroud)

第二步:使用ContentBlock而不是TextBlock.将字符串(您将用于textBlock)传递给内容块,如下所示:

<ContentControl
               Margin="7,0,0,0"
               HorizontalAlignment="Left"
               VerticalAlignment="Center"
               Content="{Binding Description, Converter={StaticResource CONVERTERS_StringToXaml}, Mode=OneTime}">
</ContentControl>
Run Code Online (Sandbox Code Playgroud)

第三步:确保传入的测试用|~S~|和标记|~E~|.让突出显示开始!

注意:
您可以更改运行中的样式以确定文本突出显示的内容和方式
确保将Converter类添加到命名空间和资源中.这可能还需要重建才能正常工作.


Dea*_*alk 6

奇怪的是,我最近写了一篇文章来解决同样的问题。这是一个具有相同的属性为TextBlock的自定义控件(所以你可以互换是出去TextBlock无论你需要它),它有一个额外的属性,您可以绑定到叫HighLightText,何地的价值HighLightText在被发现mainText属性(不区分大小写),突出显示。

这是一个相当直接的控件创建,你可以在这里找到这篇文章:

具有搜索字符串匹配的 WPF TextBlock

以及作为解决方案的完整代码:

SearchMatchTextblock(GitHub)

  • 您提供的链接已失效。 (2认同)

Ble*_*ose 6

与其他解决方案的差异

  • 更容易重用 -> 附加行为而不是自定义控件
  • MVVM 友好 -> 没有背后的代码
  • 两种方式都有效!-> 更改要突出显示的术语或文本,都会更新文本块中的突出显示。我检查的其他解决方案有问题,即更改文本不会重新应用突出显示。仅更改突出显示的术语/搜索文本有效。

如何使用

  • 重要提示:不要再使用Text="blabla"TextBlock的常规属性。而是将您的文本绑定到HighlightTermBehavior.Text="blabla".
  • 像这样将附加属性添加到您的 TextBlock
<TextBlock local:HighlightTermBehavior.TermToBeHighlighted="{Binding MyTerm}"
           local:HighlightTermBehavior.Text="{Binding MyText}" />
Run Code Online (Sandbox Code Playgroud)

或硬编码

<TextBlock local:HighlightTermBehavior.TermToBeHighlighted="highlight this"
           local:HighlightTermBehavior.Text="bla highlight this bla" />
Run Code Online (Sandbox Code Playgroud)

添加这个类

  • 要更改突出显示的类型,只需更改这些方法:
    AddPartToTextBlock()对于突出显示的文本的非突出
    AddHighlightedPartToTextBlock()显示的文本。
  • 目前突出显示的是FontWeights.ExtraBold,非突出显示的文本是FontWeights.Light
  • 如果没有 IDE,可能很难阅读,抱歉。
public static class HighlightTermBehavior
{
    public static readonly DependencyProperty TextProperty = DependencyProperty.RegisterAttached(
        "Text",
        typeof(string),
        typeof(HighlightTermBehavior),
        new FrameworkPropertyMetadata("", OnTextChanged));

    public static string GetText(FrameworkElement frameworkElement)               => (string) frameworkElement.GetValue(TextProperty);
    public static void   SetText(FrameworkElement frameworkElement, string value) => frameworkElement.SetValue(TextProperty, value);


    public static readonly DependencyProperty TermToBeHighlightedProperty = DependencyProperty.RegisterAttached(
        "TermToBeHighlighted",
        typeof(string),
        typeof(HighlightTermBehavior),
        new FrameworkPropertyMetadata("", OnTextChanged));

    public static string GetTermToBeHighlighted(FrameworkElement frameworkElement)
    {
        return (string) frameworkElement.GetValue(TermToBeHighlightedProperty);
    }

    public static void SetTermToBeHighlighted(FrameworkElement frameworkElement, string value)
    {
        frameworkElement.SetValue(TermToBeHighlightedProperty, value);
    }


    private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is TextBlock textBlock)
            SetTextBlockTextAndHighlightTerm(textBlock, GetText(textBlock), GetTermToBeHighlighted(textBlock));
    }

    private static void SetTextBlockTextAndHighlightTerm(TextBlock textBlock, string text, string termToBeHighlighted)
    {
        textBlock.Text = string.Empty;

        if (TextIsEmpty(text))
            return;

        if (TextIsNotContainingTermToBeHighlighted(text, termToBeHighlighted))
        {
            AddPartToTextBlock(textBlock, text);
            return;
        }

        var textParts = SplitTextIntoTermAndNotTermParts(text, termToBeHighlighted);

        foreach (var textPart in textParts)
            AddPartToTextBlockAndHighlightIfNecessary(textBlock, termToBeHighlighted, textPart);
    }

    private static bool TextIsEmpty(string text)
    {
        return text.Length == 0;
    }

    private static bool TextIsNotContainingTermToBeHighlighted(string text, string termToBeHighlighted)
    {
        return text.Contains(termToBeHighlighted, StringComparison.Ordinal) == false;
    }

    private static void AddPartToTextBlockAndHighlightIfNecessary(TextBlock textBlock, string termToBeHighlighted, string textPart)
    {
        if (textPart == termToBeHighlighted)
            AddHighlightedPartToTextBlock(textBlock, textPart);
        else
            AddPartToTextBlock(textBlock, textPart);
    }

    private static void AddPartToTextBlock(TextBlock textBlock, string part)
    {
        textBlock.Inlines.Add(new Run {Text = part, FontWeight = FontWeights.Light});
    }

    private static void AddHighlightedPartToTextBlock(TextBlock textBlock, string part)
    {
        textBlock.Inlines.Add(new Run {Text = part, FontWeight = FontWeights.ExtraBold});
    }


    public static List<string> SplitTextIntoTermAndNotTermParts(string text, string term)
    {
        if (text.IsNullOrEmpty())
            return new List<string>() {string.Empty};

        return Regex.Split(text, $@"({Regex.Escape(term)})")
                    .Where(p => p != string.Empty)
                    .ToList();
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @shmoltz为了使这种情况不区分大小写,您还需要更改“TextIsNotContainingTermToBeHighlighted”和“AddPartToTextBlockAndHighlightIfNecessary”以比较不区分大小写。 (2认同)