【问题标题】:Highlighting keywords in a richtextbox in WPF [duplicate]在WPF中突出显示富文本框中的关键字[重复]
【发布时间】:2015-06-01 17:04:58
【问题描述】:

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

我已经设法让他的界面,它现在可以跟踪这个词出现了多少次,但我真的不知道如何突出关键字出现的位置。我将在下面发布我的代码,非常感谢有关如何在富文本框中搜索和突出显示文本的任何帮助。由于这是在 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;
    }
}

这是我的主窗口

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();
    }
}

【问题讨论】:

    标签: c# wpf string text highlighting


    【解决方案1】:

    这里是获取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);
             }
         }
    

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

    最后,轻松突出您的文字。

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

    【讨论】:

    • 简单的问题,pattern 到底做了什么
    • 喜欢在正则表达式方法中使用它的目的是什么
    • 'Regex.Matches' 用于从字符串中提取与模式匹配的单词。供您参考:msdn.microsoft.com/en-us/library/…
    【解决方案2】:

    遇到此需求,但找不到任何合适的解决方案。 (使用 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 / 辅助方法

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

    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>
    

    外观

    【讨论】:

    • 根据代码在this 上的代码TryRemoveAdorner 无法编译。还有TryAddAdornerGenericAdorner
    • 感谢您的更新,所有其他答案都集中在TextBlock,您的答案是独一无二的。
    • 这也可以应用于已经包含富文本格式文本的richTextBox吗?如果是,我需要修改什么?顺便说一句:我收到一个错误,GenericAdorner 中的 Point 参数丢失
    猜你喜欢
    • 2014-02-09
    • 2013-01-23
    • 1970-01-01
    • 2013-04-07
    • 2013-01-05
    • 1970-01-01
    • 2012-06-26
    • 1970-01-01
    • 2010-11-08
    相关资源
    最近更新 更多