在 WPF 中突出显示富文本框中的关键字

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/29316449/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 13:02:54  来源:igfitidea点击:

Highlighting keywords in a richtextbox in WPF

c#wpfstringtexthighlighting

提问by Needham

I'm making a program which needs to look through a paragraph of text and find how many times a certain keyword/keywords appear. It also has to highlight each of these key words in the text.

我正在制作一个程序,它需要查看一段文本并找出某个关键字/关键字出现的次数。它还必须突出显示文本中的每个关键词。

I have managed to make he interface and it can now track how many times the word appears but I am really stuck for how to highlight where the keywords appear. I will post my code below, any help is greatly appreciated on how to search for and highlight text inside a richtextbox. Since this is in WPF the obvious richtextbox.find() is not avaliable for use.

我已经设法制作了他的界面,它现在可以跟踪单词出现的次数,但我真的很困惑如何突出显示关键字出现的位置。我将在下面发布我的代码,非常感谢有关如何在富文本框中搜索和突出显示文本的任何帮助。由于这是在 WPF 中,显然 Richtextbox.find() 不可用。

class TextAnalyser
{
    public int FindNumberOfOccurances(List<string> keywords, string email)
    {
        int occurances = 0;
        foreach (string keyword in keywords)
        {
            occurances += email.ToUpper().Split(new string[] { keyword.ToUpper() }, StringSplitOptions.None).Count() - 1; 
        }
        return occurances;
    }

    public void TurnTextRed(List<string> keywords, string email, RichTextBox TextBox)
    {
        foreach(string keyword in keywords)
        {
        }
    }

    public List<string> ConvertTextToList(string text)
    {
        char[] splitChars = {};
        string[] ArrayText = text.Split( splitChars, StringSplitOptions.RemoveEmptyEntries);
        return ArrayText.ToList<string>();
    }

    public string GetStringFromTextBox(RichTextBox TextBox)
    {
        var textRange = new TextRange(
            TextBox.Document.ContentStart,
            TextBox.Document.ContentEnd
        );
        return textRange.Text;
    }
}

And here is my Main Window

这是我的主窗口

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void AnalyseButton_Click(object sender, RoutedEventArgs e)
    {
        var textTool = new TextAnalyser();
        var keyWords = textTool.ConvertTextToList(textTool.GetStringFromTextBox(WordTextBox).Trim());
        var email = textTool.GetStringFromTextBox(EmailTextBox).Trim();
        int usesOfWord = textTool.FindNumberOfOccurances(keyWords, email);
        Occurances.Text = usesOfWord.ToString();
    }
}

回答by HungDL

Here is the method is used to get all of word in richtextbox's document.

这是用于获取richtextbox文档中所有单词的方法。

 public static IEnumerable<TextRange> GetAllWordRanges(FlowDocument document)
     {
         string pattern = @"[^\W\d](\w|[-']{1,2}(?=\w))*";
         TextPointer pointer = document.ContentStart;
         while (pointer != null)
         {
             if (pointer.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.Text)
             {
                 string textRun = pointer.GetTextInRun(LogicalDirection.Forward);
                 MatchCollection matches = Regex.Matches(textRun, pattern);
                 foreach (Match match in matches)
                 {
                     int startIndex = match.Index;
                     int length = match.Length;
                     TextPointer start = pointer.GetPositionAtOffset(startIndex);
                     TextPointer end = start.GetPositionAtOffset(length);
                     yield return new TextRange(start, end);
                 }
             }

             pointer = pointer.GetNextContextPosition(LogicalDirection.Forward);
         }
     }

You can change the pattern which is used to split words.

您可以更改用于拆分单词的模式。

At last, easy to highlight your words.

最后,轻松突出您的话。

  IEnumerable<TextRange> wordRanges = GetAllWordRanges(RichTextBox.Document);
        foreach (TextRange wordRange in wordRanges)
        {
            if (wordRange.Text == "keyword")
            {
                wordRange.ApplyPropertyValue(TextElement.ForegroundProperty, Brushes.Red);
            }
        }

回答by JordanTDN

Ran across a need for this and couldn't find any suitable solutions. (Using a TextBox for binding, highlighting on the fly, multiple hits and colors, etc.) This can obviously be extended to suit your needs. This references a couple of extension methods that add/remove adorners of the specified Type T from the UIElement's adorner layer.

遇到了这个需求,找不到任何合适的解决方案。(使用 TextBox 进行绑定、动态突出显示、多次点击和颜色等)这显然可以扩展以满足您的需要。这引用了几个扩展方法,这些方法从 UIElement 的装饰器层添加/删除指定类型 T 的装饰器。

 public class HighlightRule
{
    public SolidColorBrush Brush { get; set; }
    public string MatchText { get; set; }
    public HighlightRule(SolidColorBrush solidColorBrush, string matchText)
    {
        Brush = solidColorBrush;
        MatchText = matchText;
    }
    public HighlightRule(Color color, string matchText)
    {
        Brush = new SolidColorBrush(color);
        MatchText = matchText;
    }
    public HighlightRule()
    {
        MatchText = null;
        Brush = Brushes.Black;
    }
}
public class HighlightTextBox : TextBox
{
    public List<HighlightRule> HighlightRules
    {
        get { return ( List<HighlightRule>)GetValue(HighlightRulesProperty); }
        set { SetValue(HighlightRulesProperty, value); }
    }

    // Using a DependencyProperty as the backing store for HighlightRules.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty HighlightRulesProperty =
        DependencyProperty.Register("HighlightRules", typeof(List<HighlightRule>), typeof(HighlightTextBox), new FrameworkPropertyMetadata(new List<HighlightRule>(), new PropertyChangedCallback(HighlightRulesChanged)));

    private static void HighlightRulesChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        HighlightTextBox tb = (HighlightTextBox)sender;
        tb.ApplyHighlights();
    }

    public HighlightTextBox() : base()
    {
        this.Loaded += HighlightTextBox_Loaded;
    }
    protected override void OnTextChanged(TextChangedEventArgs e)
    {
        base.OnTextChanged(e);
        ApplyHighlights();
    }
    private void HighlightTextBox_Loaded(object sender, RoutedEventArgs e)
    {
        ApplyHighlights();
    }

    static HighlightTextBox()
    {
    }
    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
    }

    public void ApplyHighlights()
    {

        this.TryRemoveAdorner<GenericAdorner>();
        foreach(HighlightRule rule in HighlightRules)
        {
            if (!string.IsNullOrEmpty(rule.MatchText) && !string.IsNullOrEmpty(Text) &&
                Text.ToLower().Contains(rule.MatchText.ToLower()))
            {
                if (base.ActualHeight != 0 && base.ActualWidth != 0)
                {
                    int indexOf = 0;
                    do
                    {
                        indexOf = Text.IndexOf(rule.MatchText, indexOf);
                        if (indexOf == -1) break;
                        Rect rect = base.GetRectFromCharacterIndex(indexOf);
                        Rect backRect = base.GetRectFromCharacterIndex(indexOf + rule.MatchText.Length - 1, true);
                        this.TryAddAdorner<GenericAdorner>(new GenericAdorner(this, new Rectangle()
                        { Height = rect.Height, Width = backRect.X - rect.X, Fill = rule.Brush, Opacity = 0.5 }));
                        indexOf++;
                    } while (true);

                }
            }
        }

    }

}

GenericAdorner / Helper Methods

GenericAdorner / Helper 方法

 public class GenericAdorner : Adorner
{
    private readonly UIElement adorner;
    private readonly Point point;
    public GenericAdorner(UIElement targetElement, UIElement adorner, Point point) : base(targetElement)
    {
        this.adorner = adorner;
        if (adorner != null)
        {
            AddVisualChild(adorner);
        }
        this.point = point;
    }
    protected override int VisualChildrenCount
    {
        get { return adorner == null ? 0 : 1; }
    }
    protected override Size ArrangeOverride(Size finalSize)
    {
        if (adorner != null)
        {
            adorner.Arrange(new Rect(point, adorner.DesiredSize));
        }
        return finalSize;
    }
    protected override Visual GetVisualChild(int index)
    {
        if (index == 0 && adorner != null)
        {
            return adorner;
        }
        return base.GetVisualChild(index);
    }
}
 public static void TryRemoveAdorner<T>(this UIElement element)
        where T:Adorner
    {
        AdornerLayer layer = AdornerLayer.GetAdornerLayer(element);
        if (layer != null)
            layer.RemoveAdorners<T>(element);
    }
    public static void RemoveAdorners<T>(this AdornerLayer layer, UIElement element)
        where T : Adorner
    {
        var adorners = layer.GetAdorners(element);
        if (adorners == null) return;
        for (int i = adorners.Length -1; i >= 0; i--)
        {
            if (adorners[i] is T)
                layer.Remove(adorners[i]);
        }
    }
    public static void TryAddAdorner<T>(this UIElement element, Adorner adorner)
        where T: Adorner
    {
        AdornerLayer layer = AdornerLayer.GetAdornerLayer(element);

        if (layer != null)
            try
            {
                layer.Add(adorner);
            }
            catch (Exception) { }
    }
    public static bool HasAdorner<T>(this AdornerLayer layer, UIElement element) where T : Adorner
    {
        var adorners = layer.GetAdorners(element);
        if (adorners == null) return false;
        for (int i = adorners.Length - 1; i >= 0; i--)
        {
            if (adorners[i] is T)
                return true;
        }
        return false;
    }
    public static void RemoveAdorners(this AdornerLayer layer, UIElement element)
    {
        var adorners = layer.GetAdorners(element);
        if (adorners == null) return;
        foreach (Adorner remove in adorners)
            layer.Remove(remove);
    }

The XAML

XAML

<local:HighlightTextBox FontFamily="Calibri" Foreground="Blue" FontSize="12" Text="Hello you!! And also hello to you!" TextWrapping="Wrap" Margin="5,3,0,0">
  <local:HighlightTextBox.HighlightRules>
         <local:HighlightRule Brush="Red" MatchText="you"/>
         <local:HighlightRule Brush="Blue" MatchText="also"/>
         </local:HighlightTextBox.HighlightRules>
</local:HighlightTextBox>

Appearance Highlight Example

外貌 突出显示示例