如何在 WPF 项目控件中显示带有突出显示的查询词的搜索结果

发布于 2024-09-19 22:48:00 字数 788 浏览 8 评论 0原文

我想在 WPF ItemsControl 中显示搜索结果,并突出显示查询词。

我使用的搜索引擎 Lucene.Net 带有荧光笔插件,返回带有查询词的字符串像这样标记:

...these <Bold>results</Bold> were found to be statistically significant...

我可以指示荧光笔插件使用任何一组标记来包装查询术语。我不限于上面示例中的 标记。对于 WPF,我可能会让这些 元素附加一个样式。

挑战在于获取给定的字符串并将其呈现为我用于搜索结果的数据模板中的“实际 XAML”。换句话说,我想看到这样的东西:

...这些结果被发现具有统计显着性...

但我正在努力解决如何将数据绑定与数据模板中 XAML 字符串的动态呈现结合起来。这里最好的方法是什么?

  1. 使用 UserControl 显示每个搜索结果并从代码隐藏中调用 XamlReader.Load()
  2. 构造一个包含搜索结果字符串的 FlowDocument 并使用 FlowDocumentScrollViewer 显示结果?
  3. 完全是别的东西……?

I'd like to display search results within a WPF ItemsControl with the query terms highlighted.

The search engine I use, Lucene.Net with the Highlighter plugin, returns strings with the query terms marked up like so:

...these <Bold>results</Bold> were found to be statistically significant...

I can instruct the Highlighter plugin to use any set of markup tags to wrap a query term. I'm not limited to the <Bold> tag in the example above. For WPF, I'd likely make these <Run/> elements with a style attached.

The challenge is to take the string I've been given and render it as if it were "actual XAML" within the datatemplate I'm using for search results. In other words, I want to see something like this:

...these results were were found to be statistically significant...

But I'm struggling with how to combine databinding with dynamic rendering of an XAML string within the datatemplate. What's the best approach here?

  1. Use a UserControl to display each search result and call XamlReader.Load() from the codebehind?
  2. Construct a FlowDocument containing the search result strings and display the results with a FlowDocumentScrollViewer?
  3. Something else entirely...?

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

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

发布评论

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

评论(3

[旋木] 2024-09-26 22:48:00

我采用了 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.");
    }
}

第二步:
使用 ContentBlock 代替 TextBlock。将字符串(您将用于 textBlock)传递到内容块,如下所示:

<ContentControl
               Margin="7,0,0,0"
               HorizontalAlignment="Left"
               VerticalAlignment="Center"
               Content="{Binding Description, Converter={StaticResource CONVERTERS_StringToXaml}, Mode=OneTime}">
</ContentControl>

第三步:
确保您传入的测试已使用 |~S~||~E~| 进行标记。让突出显示开始吧!

笔记:

您可以更改运行中的样式来确定文本的突出显示内容和方式

确保将 Converter 类添加到命名空间和资源中。这可能还需要重建才能工作。

I took dthrasers answer and took out the need for an XML parser. He does a great job explaining each of the pieces in his blog, However this didn't require me to add any extra libraries, here's how I did it.

Step one, make a converter class:

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.");
    }
}

Step two:
Instead of a TextBlock use a ContentBlock. Pass in the string (you would of used for your textBlock) to the content block, like so:

<ContentControl
               Margin="7,0,0,0"
               HorizontalAlignment="Left"
               VerticalAlignment="Center"
               Content="{Binding Description, Converter={StaticResource CONVERTERS_StringToXaml}, Mode=OneTime}">
</ContentControl>

Step three:
Make sure the test you pass in is tokenized with |~S~| and |~E~|. And let the highlighting begin!

Notes:

You can change the style in the run to determine what and how your text is highlighted

Make sure you add your Converter class to your namespace and resources. This might also require a rebuild to get working.

胡大本事 2024-09-26 22:48:00

我找到了一种使用自定义 IValueConverter 将突出显示应用于搜索结果的方法。该转换器获取文本片段,将其格式化为有效的 XAML 标记,并使用 XamlReader 将标记实例化为框架对象。

完整的解释相当长,所以我将其发布到我的博客: 突出显示 WPF TextBlock 中的查询术语

I found a way to apply highlighting to search results using a custom IValueConverter. The converter takes a text snippet, formats it into valid XAML markup, and uses a XamlReader to instantiate the markup into framework objects.

The full explanation is rather long, so I've posted it to my blog: Highlighting Query Terms in a WPF TextBlock

提笔落墨 2024-09-26 22:48:00

TextBlock 可以在其 Inlines 集合中包含多个 Run。您可以在代码或 XAML 中构建它:

<TextBlock>
    <Run>... these </Run>
    <Run FontWeight="Bold">results</Run>
    <Run> were found...</Run>
</TextBlock>

A TextBlock can contain multiple Runs in its Inlines collection. You can build it in code or in XAML:

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