基于搜索条件,如何在WPF TextBlock中高亮显示特定部分

20

我有一个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

但它似乎非常不稳定:(

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

11个回答

18

14

我采用了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的字符串)传递给content block,如下所示:

步骤二: 使用ContentBlock替代TextBlock。将串(您用于textBlock的字符串)传入content block中,就像这样:

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

步骤三: 确保您传递的文本在您想要突出显示的文本部分之前包括|~S~|,并在之后包括|~E~|。例如,在此字符串中"my text |~S~|is|~E~| good"中,会将is突出显示为黄色。

注意事项:
您可以更改运行时的样式以确定您的文本被突出显示的方式和效果。
确保将您的转换器类添加到您的命名空间和资源中。这可能需要重新构建才能使其正常工作。


13

适用于.NET Framework 4.7-4.8。可能需要进行调整才能在新的.NET版本(如.NET 6)中使用,请参见注释。

与其他解决方案的区别

  • 更易于重用 -> 使用附加行为而不是自定义控件
  • MVVM友好 -> 没有代码后台
  • 双向工作!-> 更改要突出显示的术语或文本,都会更新文本块中的突出显示。我检查过的其他解决方案存在问题,即更改文本不会重新应用突出显示。只有更改突出显示的术语/搜索文本才有效。

如何使用

  • 重要提示:不要再使用TextBlock的常规Text="blabla"属性。相反,将您的文本绑定到HighlightTermBehavior.Text="blabla"
  • 像这样将附加属性添加到TextBlock中
<TextBlock local:HighlightTermBehavior.TermToBeHighlighted="{Binding MyTerm}"
           local:HighlightTermBehavior.Text="{Binding MyText}" />

或硬编码

<TextBlock local:HighlightTermBehavior.TermToBeHighlighted="highlight this"
           local:HighlightTermBehavior.Text="bla highlight this bla" />

添加这个类

  • 要更改高亮的类型,只需更改这些方法:
    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();
    }
}

1
运行良好,我只是将 Regex.Split 更改为忽略大小写。Regex.Split(text, $@"({Regex.Escape(term)})", RegexOptions.IgnoreCase) - shmoltz
2
@shmoltz 要实现不区分大小写,您还需要更改 TextIsNotContainingTermToBeHighlightedAddPartToTextBlockAndHighlightIfNecessary 以进行不区分大小写的比较。 - Mark Jansen
为了使其构建成功,我不得不进行更改: 将 public static class HighlightTermBehavior 更改为: public class HighlightTermBehavior:UIElement - Buzz
@Buzz 奇怪。那段代码是在.NET框架4.7或4.8中使用的。也许你正在使用更新的.NET版本,需要对该代码进行一些修改。你知道你正在使用哪个.NET版本吗? - Welcor
1
@Welcor,是的,正在使用.NET Core 6.0。 - Buzz

7

恰巧的是,我最近写了一篇文章解决了同样的问题。这是一个自定义控件,具有与相同的属性(因此您可以在需要时将其替换为),并且它有一个额外的属性,您可以绑定到称为的属性上,在主

创建这个控件相当简单,您可以在这里找到完整的代码解决方案:

SearchMatchTextblock(GitHub)


2
您提供的链接已失效。 - Lion King
1
他们仍然死了。 - The Muffin Man
这篇文章已经不存在了,但是源代码还在 - 上面的 Github 链接可以正常使用。 - Dean Chalk
干得好!控件方便实用,性能也很出色。 - YantingChen

4
以下是我通过在现有的上进行扩展并添加名为的新依赖属性而得出的内容:
public class SearchHightlightTextBlock : TextBlock
{
    public SearchHightlightTextBlock() : base() { }

    public String SearchText { get { return (String)GetValue(SearchTextProperty); }
                               set { SetValue(SearchTextProperty, value); } }      

    private static void OnDataChanged(DependencyObject source,
                                      DependencyPropertyChangedEventArgs e)
    {
        TextBlock tb = (TextBlock)source;

        if (tb.Text.Length == 0)
            return;

        string textUpper = tb.Text.ToUpper();
        String toFind = ((String) e.NewValue).ToUpper();
        int firstIndex = textUpper.IndexOf(toFind);
        String firstStr = tb.Text.Substring(0, firstIndex);
        String foundStr = tb.Text.Substring(firstIndex, toFind.Length);
        String endStr = tb.Text.Substring(firstIndex + toFind.Length, 
                                         tb.Text.Length - (firstIndex + toFind.Length));

        tb.Inlines.Clear();
        var run = new Run();
        run.Text = firstStr;
        tb.Inlines.Add(run);
        run = new Run();
        run.Background = Brushes.Yellow;
        run.Text = foundStr;
        tb.Inlines.Add(run);
        run = new Run();
        run.Text = endStr;

        tb.Inlines.Add(run);
    }

    public static readonly DependencyProperty SearchTextProperty =
        DependencyProperty.Register("SearchText", 
                                    typeof(String), 
                                    typeof(SearchHightlightTextBlock), 
                                    new FrameworkPropertyMetadata(null, OnDataChanged));
}

在您看来,这个:

<view:SearchHightlightTextBlock SearchText="{Binding TextPropertyContainingTextToSearch}" 
                                Text="{Binding YourTextProperty}"/>

2

在这里,我提供另一种突出文本的方法。我有一个使用案例,需要在WPF中装饰一堆C#代码,但是我不想使用textBlock.Inlines.Add类型的语法,相反,我想动态生成高亮XAML,然后将其动态添加到WPF中的Canvas或其他容器中。

假设您想对以下代码片段进行着色并突出显示其中一部分:

public static void TestLoop(int count)
{ 
   for(int i=0;i<count;i++)
     Console.WriteLine(i);
}

假设以上代码位于名为Test.txt的文件中。 假设您想将所有C#关键字(public、static、void等)和简单类型(int、string)着色为蓝色,并将Console.WriteLine突出显示为黄色。
步骤0. 创建一个新的WPF应用程序并在名为Test.txt的文件中包含类似上述的示例代码。
步骤1. 创建一个代码高亮器类:
using System.IO;
using System.Text;

public enum HighLightType
{
    Type = 0,
    Keyword = 1,
    CustomTerm = 2
}

public class CodeHighlighter
{
    public static string[] KeyWords = { "public", "static", "void", "return", "while", "for", "if" };
    public static string[] Types = { "string", "int", "double", "long" };

    private string FormatCodeInXaml(string code, bool withLineBreak)
    {
        string[] mapAr = { "<","&lt;" , //Replace less than sign
                            ">","&gt;" }; //Replace greater than sign
        StringBuilder sb = new StringBuilder();

        using (StreamReader sr = new StreamReader(new MemoryStream(Encoding.UTF8.GetBytes(code))))
        {
            while (!sr.EndOfStream)
            {
                string line = sr.ReadLine();

                line = line.Replace("\t", "&#160;&#160;&#160;&#160;"); //Replace tabs
                line = line.Replace(" ", "&#160;"); //Replace spaces

                for (int i = 0; i < mapAr.Length; i += 2)
                    line = line.Replace(mapAr[i], mapAr[i + 1]);

                if (withLineBreak)
                    sb.AppendLine(line + "<LineBreak/>"); //Replace line breaks
                else
                    sb.AppendLine(line);
            }

        }
        return sb.ToString();
    }


    private string BuildForegroundTag(string highlightText, string color)
    {
        return "<Span Foreground=\"" + color + "\">" + highlightText + "</Span>";
    }

    private string BuildBackgroundTag(string highlightText, string color)
    {
        return "<Span Background=\"" + color + "\">" + highlightText + "</Span>";
    }

    private string HighlightTerm(HighLightType type, string term, string line)
    {
        if (term == string.Empty)
            return line;

        string keywordColor = "Blue";
        string typeColor = "Blue";
        string statementColor = "Yellow";

        if (type == HighLightType.Type)
            return line.Replace(term, BuildForegroundTag(term, typeColor));
        if (type == HighLightType.Keyword)
            return line.Replace(term, BuildForegroundTag(term, keywordColor));
        if (type == HighLightType.CustomTerm)
            return line.Replace(term, BuildBackgroundTag(term, statementColor));

        return line;
    }

    public string ApplyHighlights(string code, string customTerm)
    {
        code = FormatCodeInXaml(code, true);
        customTerm = FormatCodeInXaml(customTerm, false).Trim();

        StringBuilder sb = new StringBuilder();
        using (StreamReader sr = new StreamReader(new MemoryStream(Encoding.UTF8.GetBytes(code))))
        {
            while (!sr.EndOfStream)
            {
                string line = sr.ReadLine();

                line = HighlightTerm(HighLightType.CustomTerm, customTerm, line);

                foreach (string keyWord in KeyWords)
                    line = HighlightTerm(HighLightType.Keyword, keyWord, line);

                foreach (string type in Types)
                    line = HighlightTerm(HighLightType.Type, type, line);

                sb.AppendLine(line);
            }
        }

        return sb.ToString();

    }
}

步骤2. 在您的MainWindow.xaml中添加一个Canvas XAML标记

<Window x:Class="TestCodeVisualizer.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:TestCodeVisualizer"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">

    <Canvas Name="canvas" />
</Window>

步骤三:在您的WPF应用程序中添加以下代码(确保test.txt位于正确的位置):

using System.Text;
using System.IO;
using System.Windows;
using System.Windows.Markup;

namespace TestCodeVisualizer
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            string testText = File.ReadAllText("Test.txt");
            FrameworkElement fe = GenerateHighlightedTextBlock(testText, "Console.WriteLine");
            this.canvas.Children.Add(fe);
        }


        private FrameworkElement GenerateHighlightedTextBlock(string code, string term)
        {
            CodeHighlighter ch = new CodeHighlighter();
            string uc = "<UserControl xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>[CONTENT]</UserControl>";

            string content = "<TextBlock>" + ch.ApplyHighlights(code, term) + "</TextBlock>";
            uc = uc.Replace("[CONTENT]", content);

            FrameworkElement fe = XamlReader.Load(new System.IO.MemoryStream(Encoding.UTF8.GetBytes(uc))) as FrameworkElement;
            return fe;
        }

    }
}

2

我曾遇到类似的问题——尝试在代表报告的一堆演讲者中实现文本搜索。该报告最初是写成一个字符串的形式,我们利用FlowDocumentViewer内置的ctrl-F进行搜索。它不是很好用,有些奇怪的选项,但足够使用。

如果你只想要类似的功能,可以按照以下步骤操作:

        <FlowDocumentScrollViewer>
            <FlowDocument>
                <Paragraph FontFamily="Lucida Console" FontSize="12">
                    <Run Text="{Binding Content, Mode=OneWay}"/>
                </Paragraph>
            </FlowDocument>
        </FlowDocumentScrollViewer>

我们决定进行重写,因为报告需要与程序的其余部分保持同步,基本上每次编辑都会更改它,每次都必须重新创建整个报告意味着这非常慢。我们希望通过转移到更新所需位模型来改进此问题,但需要视图模型(而不仅仅是字符串)以便能够以明智的方式执行该操作!然而,在交换报告之前,我们希望保留搜索功能,并更进一步,在一个颜色中突出显示“当前”搜索位置,其他搜索结果在另一个颜色中突出显示。
以下是我解决方案的简化版本; 它是从TextBlock派生的类,添加了HighlightingInformation类型的依赖属性。由于它们是敏感信息,我没有包括命名空间和使用情况。
public class HighlightingTextBlock : TextBlock
{
    public static readonly DependencyProperty HighlightingProperty =
        DependencyProperty.Register("Highlighting", typeof (HighlightingInformation), typeof (HighlightingTextBlock));

    public HighlightingInformation Highlighting
    {
        get { return (HighlightingInformation)GetValue(HighlightingProperty); }
        set { SetValue(HighlightingProperty, value); }
    }

    public HighlightingTextBlock()
    {
        AddValueChangedCallBackTo(HighlightingProperty, UpdateText);
    }

    private void AddValueChangedCallBackTo(DependencyProperty property, Action updateAction)
    {
        var descriptor = DescriptorFor(property);
        descriptor.AddValueChanged(this, (src, args) => updateAction());
    }

    private DependencyPropertyDescriptor DescriptorFor(DependencyProperty property)
    {
        return DependencyPropertyDescriptor.FromProperty(property, GetType());
    }

    private void UpdateText()
    {
        var highlighting = Highlighting;
        if (highlighting == null)
            return;
        highlighting.SetUpdateMethod(UpdateText);

        var runs = highlighting.Runs;
        Inlines.Clear();
        Inlines.AddRange(runs);
    }
}

这个类可以绑定到的类型在文本和高亮列表改变时使用update方法来更新Runs列表。高亮本身看起来像这样:

public class Highlight
{
    private readonly int _length;
    private readonly Brush _colour;

    public int Start { get; private set; }

    public Highlight(int start, int length,Brush colour)
    {
        Start = start;
        _length = length;
        _colour = colour;
    }

    private string TextFrom(string currentText)
    {
        return currentText.Substring(Start, _length);
    }

    public Run RunFrom(string currentText)
    {
        return new Run(TextFrom(currentText)){Background = _colour};
    }
}

生成正确的高亮集合是一个单独的问题,我基本上通过将演示者集合视为树来解决这个问题,您可以递归地搜索内容 - 叶节点是具有内容的节点,其他节点只有子节点。如果您进行深度优先搜索,您将获得您所期望的顺序。然后,您可以基本上编写一个包装器来跟踪位置列表的结果。我不会在此处发布所有代码 - 我的回应是记录如何使wpf以MVP样式进行多颜色突出显示。

我没有在这里使用INotifyPropertyChanged或CollectionChanged,因为我们不需要将更改多播(例如,一个演示者具有多个视图)。最初,我尝试通过为文本添加一个事件更改通知和一个列表的事件更改通知来实现这一点(您还必须手动订阅INotifyCollectionChanged事件)。但是,我担心事件订阅会导致内存泄漏,并且文本和突出显示的更新不同时会导致问题。

这种方法的一个缺点是人们不应该绑定到此控件的Text属性。在真正的版本中,我添加了一些检查+异常抛出来阻止人们这样做,但是出于清晰起见,我从示例中省略了它!


0

我所需求的是,高亮必须能够完全可样式化,而不仅仅是一些预定义选项:

public partial class HighlightTextBlock : UserControl
{
    public HighlightTextBlock()
    {
        InitializeComponent();
    }

    public static readonly DependencyProperty TextBlockStyleProperty = DependencyProperty.Register(
        nameof(TextBlockStyle), typeof(Style), typeof(HighlightTextBlock), new PropertyMetadata(default(Style)));
    public Style TextBlockStyle
    {
        get { return (Style)GetValue(TextBlockStyleProperty); }
        set { SetValue(TextBlockStyleProperty, value); }
    }

    public static readonly DependencyProperty HighlightTextElementStyleProperty = DependencyProperty.Register(
        nameof(HighlightTextElementStyle), typeof(Style), typeof(HighlightTextBlock), new PropertyMetadata(default(Style)));
    public Style HighlightTextElementStyle
    {
        get { return (Style)GetValue(HighlightTextElementStyleProperty); }
        set { SetValue(HighlightTextElementStyleProperty, value); }
    }

    public static readonly DependencyProperty NormalTextElementStyleProperty = DependencyProperty.Register(
        nameof(NormalTextElementStyle), typeof(Style), typeof(HighlightTextBlock), new PropertyMetadata(default(Style)));
    public Style NormalTextElementStyle
    {
        get { return (Style)GetValue(NormalTextElementStyleProperty); }
        set { SetValue(NormalTextElementStyleProperty, value); }
    }

    public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
        nameof(Text), typeof(string), typeof(HighlightTextBlock), new PropertyMetadata(default(string), PropertyChangedCallback));
    public string Text
    {
        get { return (string)GetValue(TextProperty); }
        set { SetValue(TextProperty, value); }
    }

    public static readonly DependencyProperty HighlightProperty = DependencyProperty.Register(
        nameof(Highlight), typeof(string), typeof(HighlightTextBlock), new PropertyMetadata(default(string), PropertyChangedCallback));
    public string Highlight
    {
        get { return (string)GetValue(HighlightProperty); }
        set { SetValue(HighlightProperty, value); }
    }
    
    private static void PropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        d.EnsureType<HighlightTextBlock>().update();
    }
    
    private void update()
    {
        var highlightLength = this.Highlight?.Length ?? 0;

        if (highlightLength > 0)
        {
            var highlightOffset = this.Text?.IndexOf(this.Highlight, StringComparison.InvariantCultureIgnoreCase) ?? -1;

            if (highlightOffset > -1)
            {
                PrefixRun.Text = this.Text.Substring(0, highlightOffset);
                HighlightRun.Text = this.Text.Substring(highlightOffset, highlightLength);
                SuffixRun.Text = this.Text.Substring(highlightOffset + highlightLength);
                return;
            }
        }

        PrefixRun.Text = this.Text;
        HighlightRun.Text = null;
        SuffixRun.Text = null;
    }
}

使用PropertyChangedCallback,它被HighlightPropertyTextProperty调用。

XAML:

<UserControl x:Class="Example.HighlightTextBlock"
             x:Name="self"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008">
    <Grid>
        <TextBlock DataContext="{Binding ElementName=self}" Style="{Binding TextBlockStyle}">
            <!-- NOTE: TO avoid whitespaces when rendering Inlines, avoid them in markup (e.g. between Run tags)-->
            <TextBlock.Inlines><Run 
                                    x:Name="PrefixRun"     x:FieldModifier="private" Style="{Binding NormalTextElementStyle}"/><Run 
                                    x:Name="HighlightRun"  x:FieldModifier="private" Style="{Binding HighlightTextElementStyle}"/><Run 
                                    x:Name="SuffixRun"     x:FieldModifier="private" Style="{Binding NormalTextElementStyle}"/></TextBlock.Inlines>
        </TextBlock>
    </Grid>
</UserControl>

数据模板:

<DataTemplate x:Key="ExampleDataTemplate">
    <DataTemplate.Resources>
        <Style x:Key="HighlightTextElementStyle" TargetType="{x:Type Inline}">
            <Setter Property="Foreground" Value="DarkGray"/>
            <Setter Property="FontWeight" Value="Bold"/>
            <Setter Property="TextDecorations" Value="Underline"/>
        </Style>
        
        <Style x:Key="TextBlockStyle" TargetType="{x:Type TextBlock}" BasedOn="{StaticResource {x:Type TextBlock}}">
            <Setter Property="Foreground" Value="White"/>
            <Setter Property="HorizontalAlignment" Value="Stretch"/>
            <Setter Property="TextAlignment" Value="Left"/>
        </Style>
    </DataTemplate.Resources>

    <controls1:HighlightTextBlock Text="{Binding ExampleText}"
                                  Highlight="{Binding ExampleHighlight}"
                                  TextBlockStyle="{StaticResource TextBlockStyle}"
                                  HighlightTextElementStyle="{StaticResource HighlightTextElementStyle}"/>
</DataTemplate>

0
以下是一个高亮搜索方法,它接受你的和,然后返回包含该术语或单词的块,并将其突出显示为紫色。
    private TextBlock HighlightSearch(TextBlock textBlock, string searchTerm)
    {
        string[] words = textBlock.Text.Split(' ');

        textBlock.Text = string.Empty; 
        
        foreach (string word in words)
        {
            if (!string.IsNullOrEmpty(searchTerm) &&
                word.IndexOf(searchTerm, StringComparison.OrdinalIgnoreCase) >= 0)
            {
                textBlock.Inlines.Add(new Run($"{word} ") { Foreground = Brushes.Purple, FontWeight = FontWeights.DemiBold });
            }
            else
            {
                textBlock.Inlines.Add($"{word} ");
            }
        }

        return textBlock; 
    }

`


0

最终编写了以下代码

目前还有一些错误,但解决了问题

if (Main.IsFullTextSearch)
{
    for (int i = 0; i < runs.Count; i++)
    {
        if (runs[i] is Run)
        {
            Run originalRun = (Run)runs[i];

            if (Main.SearchCondition != null && originalRun.Text.ToLower()
                .Contains(Main.SearchCondition.ToLower()))
            {
                int pos = originalRun.Text.ToLower()
                          .IndexOf(Main.SearchCondition.ToLower());

                if (pos > 0)
                {
                    Run preRun = CloneRun(originalRun);
                    Run postRun = CloneRun(originalRun);

                    preRun.Text = originalRun.Text.Substring(0, pos);
                    postRun.Text = originalRun.Text
                        .Substring(pos + Main.SearchCondition.Length);

                    runs.Insert(i - 1 < 0 ? 0 : i - 1, preRun);
                    runs.Insert(i + 1, new Run(" "));
                    runs.Insert(i + 2, postRun);

                    originalRun.Text = originalRun.Text
                        .Substring(pos, Main.SearchCondition.Length);

                    SolidColorBrush brush = new SolidColorBrush(Colors.Yellow);
                    originalRun.Background = brush;

                    i += 3;
                }
            }
        }
    }
}

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