如何突出显示具有不同于 RichTextBox 文本中所有其他选择的颜色的单词或短语?
How to highlight a word or phrase with a color different from all the other selections in a RichTextBox text?
主要目标是让用户在所有其他选定的单词或短语中将一个选项识别为当前选项。
private void numericUpDown1_ValueChanged(object sender, EventArgs e)
{
if (results.Count > 0)
{
richTextBox1.SelectionStart = results[(int)numericUpDown1.Value - 1];
richTextBox1.SelectionLength = textBox1.Text.Length;
richTextBox1.SelectionColor = Color.Red;
richTextBox1.ScrollToCaret();
}
}
这样的话,在截图中,有3个搜索词的结果:System
。所有结果都显示为黄色。
如果我将 NumericUpDown 值更改为 2,它会将结果中的第二个 System
单词着色为红色,然后当将 NumericUpDown 值更改为 3 时,它也会将最后一个系统结果着色为红色.
我的问题是 2 和 3 都是红色的:我只希望当前选择的单词是红色的,所有其他匹配项应该使用默认颜色。
一些有助于构建 class 可以处理文本选择的对象的建议。
这个 class 应该包含大部分(或全部)在给定文本中搜索关键字所需的逻辑,维护找到的匹配列表,它们的位置和长度并允许基本 此匹配项列表的导航,以及可以轻松扩展功能的其他配置。
此处,TextSearcher
1 class 构造函数需要一个 RichTextBox 控件作为参数之一,外加用于正常选择的 Color 和一个navigated.
匹配列表时用于突出显示当前匹配的颜色
▶ 搜索功能由 Regex.Matches() method, which return a MatchCollection of Match 对象处理,非常方便,因为每个 Match
包含文本中匹配项的位置 (Index
属性)及其长度 (Length
属性).
▶ 导航功能由 BindingSource 对象提供。它的 DataSource
属性 设置为 Regex.Matches()
返回的 MatchCollection
并且每次搜索一组新关键字时都会重置。
此 class 在初始化时提供 MoveNext()
、MovePrevious()
、MoveLast()
和MoveFirst()
方法,加上一个Position
属性,它标识集合中当前对象的索引 - Match
对象,此处。
▶ CaseSensite
属性 用于执行区分大小写或不区分大小写的搜索。添加是为了表明可以很简单地使用基于现有功能的更多功能来扩展基本功能(例如,文化 sensitive/insensitive 搜索)。
要初始化 TextSearcher class,将 RichTextBox 控件的实例和两个 Color 值传递给它的构造函数:
TextSearcher matchFinder = null;
public SomeForm()
{
InitializeComponent();
matchFinder = new TextSearcher(richTextBox1, Color.Yellow, Color.Red);
}
在示例中,您可以看到四个控件:
- 上一个 (
btnPrevious
) 和下一个 (btnNext
) 按钮,用于导航匹配列表。
- 一个 NumericUpDown (
nudGotoMatch
),用于跳转到特定的匹配键。
- 一个文本框(
txtSearch
),用于输入一个或多个关键字,用竖线分隔(竖线字符用于指定正则表达式中的备用匹配项,可以用其他内容替换 UI).
由于 TextSearcher
class 包含所有搜索和导航逻辑,因此在前端激活这些控件功能所需的代码很少,只需标准 UI 要求(例如在 TextBox 控件中按下 Enter 键时设置 e.SuppressKeyPress = true
)。
private void txtSearch_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter) {
string keywords = (sender as Control).Text;
e.SuppressKeyPress = true;
if (!matchFinder.CurrentKeywords.Equals(keyword)) {
nudGotoMatch.Maximum = matchFinder.Search(keywords);
}
}
}
private void nudGotoMatch_ValueChanged(object sender, EventArgs e)
{
if (matchFinder.Matches.Count > 0) {
matchFinder.GotoMatch((int)nudGotoMatch.Value - 1);
}
}
private void btnPrevious_Click(object sender, EventArgs e)
{
matchFinder.PreviousMatch();
}
private void btnNext_Click(object sender, EventArgs e)
{
matchFinder.NextMatch();
}
它是这样工作的:
TextSearcher
class:
using System.Collections.Generic;
using System.Drawing;
using System.Text.RegularExpressions;
using System.Windows.Forms;
private class TextSearcher
{
private BindingSource m_bsMatches = null;
private RichTextBox m_Rtb = null;
public TextSearcher(RichTextBox rtb) : this(rtb, Color.Yellow, Color.Red) { }
public TextSearcher(RichTextBox rtb, Color selectionColor, Color currentColor)
{
m_Rtb = rtb;
SelectionColor = selectionColor;
CurrentColor = currentColor;
}
public string CurrentKeywords { get; private set; } = string.Empty;
public bool CaseSensitive { get; set; } = true;
public int CurrentIndex => m_bsMatches.Position;
public Match CurrentMatch => (Match)m_bsMatches.Current;
public MatchCollection Matches { get; private set; }
public Color SelectionColor { get; set; }
public Color CurrentColor { get; set; }
public void GotoMatch(int position)
{
SelectText(false);
m_bsMatches.Position = Math.Max(Math.Min(m_bsMatches.Count, position), 0);
SelectText(true);
}
public void NextMatch()
{
SelectText(false);
if (m_bsMatches != null && m_bsMatches.Position == m_bsMatches.Count - 1) {
m_bsMatches.MoveFirst();
}
else { m_bsMatches.MoveNext(); }
SelectText(true);
}
public void PreviousMatch()
{
SelectText(false);
if (m_bsMatches != null && m_bsMatches.Position > 0) {
m_bsMatches.MovePrevious();
}
else { m_bsMatches.MoveLast(); }
SelectText(true);
}
public int Search(string keywords)
{
if (CurrentKeywords.Equals(keywords)) return Matches.Count;
m_bsMatches?.Dispose();
CurrentKeywords = keywords;
var options = RegexOptions.Multiline |
(CaseSensitive ? RegexOptions.IgnoreCase : RegexOptions.None);
Matches = Regex.Matches(m_Rtb.Text, keywords, options);
if (Matches != null) {
m_Rtb.SelectAll();
m_Rtb.SelectionColor = m_Rtb.ForeColor;
m_Rtb.SelectionStart = 0;
m_bsMatches = new BindingSource(Matches, null);
SelectKeywords();
return Matches.Count;
}
return 0;
}
private void SelectKeywords()
{
foreach (Match m in Matches) {
SelectText(false);
NextMatch();
}
m_bsMatches.MoveFirst();
}
private void SelectText(bool current)
{
m_Rtb.Select(CurrentMatch.Index, CurrentMatch.Length);
m_Rtb.SelectionColor = current ? CurrentColor : SelectionColor;
}
}
1 - 我知道,好名字!我花了一段时间才想出它,所以请不要更改它:)
主要目标是让用户在所有其他选定的单词或短语中将一个选项识别为当前选项。
private void numericUpDown1_ValueChanged(object sender, EventArgs e)
{
if (results.Count > 0)
{
richTextBox1.SelectionStart = results[(int)numericUpDown1.Value - 1];
richTextBox1.SelectionLength = textBox1.Text.Length;
richTextBox1.SelectionColor = Color.Red;
richTextBox1.ScrollToCaret();
}
}
这样的话,在截图中,有3个搜索词的结果:System
。所有结果都显示为黄色。
如果我将 NumericUpDown 值更改为 2,它会将结果中的第二个 System
单词着色为红色,然后当将 NumericUpDown 值更改为 3 时,它也会将最后一个系统结果着色为红色.
我的问题是 2 和 3 都是红色的:我只希望当前选择的单词是红色的,所有其他匹配项应该使用默认颜色。
一些有助于构建 class 可以处理文本选择的对象的建议。
这个 class 应该包含大部分(或全部)在给定文本中搜索关键字所需的逻辑,维护找到的匹配列表,它们的位置和长度并允许基本 此匹配项列表的导航,以及可以轻松扩展功能的其他配置。
此处,TextSearcher
1 class 构造函数需要一个 RichTextBox 控件作为参数之一,外加用于正常选择的 Color 和一个navigated.
▶ 搜索功能由 Regex.Matches() method, which return a MatchCollection of Match 对象处理,非常方便,因为每个 Match
包含文本中匹配项的位置 (Index
属性)及其长度 (Length
属性).
▶ 导航功能由 BindingSource 对象提供。它的 DataSource
属性 设置为 Regex.Matches()
返回的 MatchCollection
并且每次搜索一组新关键字时都会重置。
此 class 在初始化时提供 MoveNext()
、MovePrevious()
、MoveLast()
和MoveFirst()
方法,加上一个Position
属性,它标识集合中当前对象的索引 - Match
对象,此处。
▶ CaseSensite
属性 用于执行区分大小写或不区分大小写的搜索。添加是为了表明可以很简单地使用基于现有功能的更多功能来扩展基本功能(例如,文化 sensitive/insensitive 搜索)。
要初始化 TextSearcher class,将 RichTextBox 控件的实例和两个 Color 值传递给它的构造函数:
TextSearcher matchFinder = null;
public SomeForm()
{
InitializeComponent();
matchFinder = new TextSearcher(richTextBox1, Color.Yellow, Color.Red);
}
在示例中,您可以看到四个控件:
- 上一个 (
btnPrevious
) 和下一个 (btnNext
) 按钮,用于导航匹配列表。 - 一个 NumericUpDown (
nudGotoMatch
),用于跳转到特定的匹配键。 - 一个文本框(
txtSearch
),用于输入一个或多个关键字,用竖线分隔(竖线字符用于指定正则表达式中的备用匹配项,可以用其他内容替换 UI).
由于 TextSearcher
class 包含所有搜索和导航逻辑,因此在前端激活这些控件功能所需的代码很少,只需标准 UI 要求(例如在 TextBox 控件中按下 Enter 键时设置 e.SuppressKeyPress = true
)。
private void txtSearch_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter) {
string keywords = (sender as Control).Text;
e.SuppressKeyPress = true;
if (!matchFinder.CurrentKeywords.Equals(keyword)) {
nudGotoMatch.Maximum = matchFinder.Search(keywords);
}
}
}
private void nudGotoMatch_ValueChanged(object sender, EventArgs e)
{
if (matchFinder.Matches.Count > 0) {
matchFinder.GotoMatch((int)nudGotoMatch.Value - 1);
}
}
private void btnPrevious_Click(object sender, EventArgs e)
{
matchFinder.PreviousMatch();
}
private void btnNext_Click(object sender, EventArgs e)
{
matchFinder.NextMatch();
}
它是这样工作的:
TextSearcher
class:
using System.Collections.Generic;
using System.Drawing;
using System.Text.RegularExpressions;
using System.Windows.Forms;
private class TextSearcher
{
private BindingSource m_bsMatches = null;
private RichTextBox m_Rtb = null;
public TextSearcher(RichTextBox rtb) : this(rtb, Color.Yellow, Color.Red) { }
public TextSearcher(RichTextBox rtb, Color selectionColor, Color currentColor)
{
m_Rtb = rtb;
SelectionColor = selectionColor;
CurrentColor = currentColor;
}
public string CurrentKeywords { get; private set; } = string.Empty;
public bool CaseSensitive { get; set; } = true;
public int CurrentIndex => m_bsMatches.Position;
public Match CurrentMatch => (Match)m_bsMatches.Current;
public MatchCollection Matches { get; private set; }
public Color SelectionColor { get; set; }
public Color CurrentColor { get; set; }
public void GotoMatch(int position)
{
SelectText(false);
m_bsMatches.Position = Math.Max(Math.Min(m_bsMatches.Count, position), 0);
SelectText(true);
}
public void NextMatch()
{
SelectText(false);
if (m_bsMatches != null && m_bsMatches.Position == m_bsMatches.Count - 1) {
m_bsMatches.MoveFirst();
}
else { m_bsMatches.MoveNext(); }
SelectText(true);
}
public void PreviousMatch()
{
SelectText(false);
if (m_bsMatches != null && m_bsMatches.Position > 0) {
m_bsMatches.MovePrevious();
}
else { m_bsMatches.MoveLast(); }
SelectText(true);
}
public int Search(string keywords)
{
if (CurrentKeywords.Equals(keywords)) return Matches.Count;
m_bsMatches?.Dispose();
CurrentKeywords = keywords;
var options = RegexOptions.Multiline |
(CaseSensitive ? RegexOptions.IgnoreCase : RegexOptions.None);
Matches = Regex.Matches(m_Rtb.Text, keywords, options);
if (Matches != null) {
m_Rtb.SelectAll();
m_Rtb.SelectionColor = m_Rtb.ForeColor;
m_Rtb.SelectionStart = 0;
m_bsMatches = new BindingSource(Matches, null);
SelectKeywords();
return Matches.Count;
}
return 0;
}
private void SelectKeywords()
{
foreach (Match m in Matches) {
SelectText(false);
NextMatch();
}
m_bsMatches.MoveFirst();
}
private void SelectText(bool current)
{
m_Rtb.Select(CurrentMatch.Index, CurrentMatch.Length);
m_Rtb.SelectionColor = current ? CurrentColor : SelectionColor;
}
}
1 - 我知道,好名字!我花了一段时间才想出它,所以请不要更改它:)