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

9
我希望能够在WPF ItemsControl中显示搜索结果,并突出显示查询词。
我使用的搜索引擎是Lucene.Net,配合Highlighter插件返回标记有查询词的字符串。
...these <Bold>results</Bold> were found to be statistically significant...

我可以指示Highlighter插件使用任何一组标记来包装查询项。我不局限于上述示例中的<Bold>标记。对于WPF,我可能会使用带有样式的<Run/>元素。挑战在于将我收到的字符串呈现为数据模板中使用的“实际XAML”。换句话说,我想看到像这样的东西:

...这些结果被发现具有统计学意义...

但我正在努力将数据绑定与动态渲染数据模板内的XAML字符串相结合。这里最好的方法是什么?
  1. 使用UserControl显示每个搜索结果并从代码后台调用XamlReader.Load()
  2. 构建包含搜索结果字符串的FlowDocument,并使用FlowDocumentScrollViewer显示结果?
  3. 还有其他什么方法吗?
3个回答

12

我采用了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。向ContentBlock传递字符串(与您用于textBlock的相同字符串),如下所示:

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

第三步: 确保您传递的测试已使用|〜S〜||〜E〜|进行标记化。然后让高亮开始!

注:
您可以更改运行中的样式以确定如何高亮显示文本。
确保将Converter类添加到命名空间和资源中。这可能需要重新构建才能正常工作。


9
我找到了一种使用自定义IValueConverter应用高亮显示到搜索结果的方法。该转换器将文本片段格式化为有效的XAML标记,并使用XamlReader将标记实例化为框架对象。
完整的解释比较长,因此我已经在我的博客上发布了它:在WPF TextBlock中突出显示查询术语

4
收到500 Http错误,请问你(或其他人)可以修复链接吗? - Rand Random
1
抱歉,我的WordPress网站出了点问题。现在已经解决了。 - dthrasher
1
链接又挂了。已编辑链接,指向页面的缓存版本,供参考。 - ardila

2

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

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

我的问题可能没有表达清楚。棘手的部分是我需要在运行时将字符串转换为XAML,而不是编译时。 - dthrasher
也许我错过了什么,但在我的例子中,使用一些正则表达式构建类似于XAML字符串并使用您的第一种方法似乎是可行的。我建议的解决方案是在运行时构建TextBlock,并使用Runs填充其Inlines集合。将样式添加到突出显示的Runs是替换FontWeight="Bold"的解决方案。 - Mart
谢谢,@Mart。你的建议让我找对了方向。我的回答描述了我使用的方法。 - dthrasher

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接