我有同样的需求。这里是一个可工作的WinForms应用程序来演示。解决这个问题的灵感来自于CodeGuru上的
"RichTextBox reverse find"线程。
在我的演示中,RTB预加载了文本并且是只读的,即,我没有尝试解决使用可编辑的RTB实现正向和反向查找所涉及的问题。
![demo program screen shot](https://istack.dev59.com/ETBOh.webp)
该表单具有以下控件:
- 名为 objButtonFind 的 Forms.Button。文本 = "查找"
- 名为 objTextBoxSearchWord 的 Forms.TextBox
- 名为 objReverse 的 Forms.Checkbox。文本 = "反向"
- 名为 objCheckBoxMatchCase 的 Forms.Checkbox。文本 = "匹配大小写"
- 名为 objCheckBoxWholeWord 的 Forms.Checkbox。文本 = "全词匹配"
- 名为 label1 的 Forms.Label。文本 = "RTB 起始位置"
- 名为 objTextBoxStartPos 的 Forms.TextBox
- 派生自 Forms.RichTextBox 的 Class ClsRichTextBox
ClsProgram.cs
using System;
using System.Windows.Forms;
namespace RichTextBoxFindWithReverse
{
static class ClsProgram
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new ClsFormMain());
}
}
public static class ControlExtensions
{
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern bool LockWindowUpdate(IntPtr hWndLock);
public static void Suspend(this Control control)
{
LockWindowUpdate(control.Handle);
}
public static void Resume(this Control control)
{
LockWindowUpdate(IntPtr.Zero);
}
}
}
ClsFormMain.cs
using System;
using System.Windows.Forms;
namespace RichTextBoxFindWithReverse
{
public partial class ClsFormMain : Form
{
public ClsFormMain()
{
InitializeComponent();
objRichTextBox.SelectionDataIsInteresting += ObjRichTextBox_SelectionDataIsInteresting;
}
private void Form1_Load(object sender, EventArgs e)
{
objRichTextBox.Text = "cat\ndog\nsnail\nOtter\ntigerelephant\ncatcatcat\ncatcatdog\ncatcatsnail\ncatcatOtter\ncatcattiger\ncatcatelephant\ncatdogcat\ncatdogdog\ncatdogsnail\ncatdogOtter\ncatdogtiger\ncatdogelephant\ncatsnailcat\ncatsnaildog\ncatsnailsnail\ncatsnailOtter\ncatsnailtiger\ncatsnailelephant\ncatOttercat\ncatOtterdog\ncatOttersnail\ncatOtterOtter\ncatOttertiger\ncatOtterelephant\ncattigercat\ncattigerdog\ncattigersnail";
objButtonFind.Enabled = false;
objReverse.Enabled = false;
objCheckBoxMatchCase.Enabled = false;
objCheckBoxWholeWord.Enabled = false;
}
private void ObjTextBoxSearchWord_TextChanged(object sender, EventArgs e)
{
if (objRichTextBox.Text.Length > 0 && objTextBoxSearchWord.Text.Length > 0)
{
objButtonFind.Enabled = true;
objReverse.Enabled = true;
objCheckBoxMatchCase.Enabled = true;
objCheckBoxWholeWord.Enabled = true;
}
else
{
objButtonFind.Enabled = false;
objReverse.Enabled = false;
objCheckBoxMatchCase.Enabled = false;
objCheckBoxWholeWord.Enabled = false;
}
}
private void ObjButtonFind_Click(object sender, EventArgs e)
{
string options = "";
if (!objCheckBoxMatchCase.Checked && !objCheckBoxWholeWord.Checked)
{
options = "Don't match case.\nMatch on partial word or whole word.";
}
else if (!objCheckBoxMatchCase.Checked && objCheckBoxWholeWord.Checked)
{
options = "Don't match case.\nMatch on whole word only.";
}
else if (objCheckBoxMatchCase.Checked && !objCheckBoxWholeWord.Checked)
{
options = "Match case.\nMatch on partial word or whole word.";
}
else
{
options = "Match case.\nMatch on whole word only.";
}
bool found = objRichTextBox.FindTextCustom(objTextBoxSearchWord.Text, objReverse.Checked, objCheckBoxMatchCase.Checked, objCheckBoxWholeWord.Checked);
if (!found)
{
System.Windows.Forms.MessageBox.Show(string.Format("Can't find '{0}'.\n\nYour options:\n\n{1}", objTextBoxSearchWord.Text, options), "RichTextBox Find With Reverse", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
private void ObjRichTextBox_SelectionDataIsInteresting(object sender, ClsRichTextBoxSelectionArgs e)
{
objTextBoxStartPos.Text = e.SelectionStart.ToString();
}
}
}
ClsRichTextBox.cs
using System;
using System.Drawing;
using System.Windows.Forms;
namespace RichTextBoxFindWithReverse
{
class ClsRichTextBox : RichTextBox
{
ClsFindMetadata objFindMetadata = null;
ClsRichTextBoxSelectionArgs objRichTextBoxSelectionArgs = null;
public ClsRichTextBox() : base()
{
SelectionChanged += ClsRichTextBox_SelectionChanged;
objFindMetadata = new ClsFindMetadata();
objRichTextBoxSelectionArgs = new ClsRichTextBoxSelectionArgs();
}
private void ClsRichTextBox_SelectionChanged(object sender, EventArgs e)
{
ClearLastFind();
objRichTextBoxSelectionArgs.Set(SelectionStart);
OnSelectionDataIsInteresting(objRichTextBoxSelectionArgs);
}
public void ClearLastFind()
{
SelectionChanged -= ClsRichTextBox_SelectionChanged;
ControlExtensions.Suspend(this);
int saveSelectionStart = SelectionStart;
if (objFindMetadata.findStart != -1)
{
objFindMetadata.ClearFind();
}
if (objFindMetadata.highLightStart != -1)
{
Select(objFindMetadata.highLightStart, objFindMetadata.highLightLength);
objFindMetadata.ClearHighLight();
SelectionBackColor = Color.White;
SelectionLength = 0;
}
SelectionStart = saveSelectionStart;
ControlExtensions.Resume(this);
SelectionChanged += ClsRichTextBox_SelectionChanged;
}
public bool FindTextCustom(string searchText, bool isReverse, bool isMatchCase, bool isWholeWord)
{
int previousSaveFindIndex = objFindMetadata.findStart;
int previousSaveFindLength = objFindMetadata.findLength;
int localForwardOffset = 1;
int saveSelectionStart = SelectionStart;
int saveSelectionLength = SelectionLength;
int indexToplineCharOne = GetCharIndexFromPosition(new Point(0, 0));
bool found = false;
SelectionChanged -= ClsRichTextBox_SelectionChanged;
ControlExtensions.Suspend(this);
SelectionStart = saveSelectionStart;
if (saveSelectionStart == 0 && objFindMetadata.findStart == -1)
{
localForwardOffset = 0;
}
if (!isReverse && !isMatchCase && !isWholeWord)
{
objFindMetadata.findStart = Find(searchText, Math.Min(SelectionStart + localForwardOffset, TextLength), Text.Length, RichTextBoxFinds.None);
}
else if (!isReverse && !isMatchCase && isWholeWord)
{
objFindMetadata.findStart = Find(searchText, Math.Min(SelectionStart + localForwardOffset, TextLength), Text.Length, RichTextBoxFinds.WholeWord);
}
else if (!isReverse && isMatchCase && !isWholeWord)
{
objFindMetadata.findStart = Find(searchText, Math.Min(SelectionStart + localForwardOffset, TextLength), Text.Length, RichTextBoxFinds.MatchCase);
}
else if (!isReverse && isMatchCase && isWholeWord)
{
objFindMetadata.findStart = Find(searchText, Math.Min(SelectionStart + localForwardOffset, TextLength), Text.Length, RichTextBoxFinds.MatchCase | RichTextBoxFinds.WholeWord);
}
else if (isReverse && !isMatchCase && !isWholeWord)
{
objFindMetadata.findStart = Find(searchText, 0, SelectionStart, RichTextBoxFinds.Reverse);
}
else if (isReverse && !isMatchCase && isWholeWord)
{
objFindMetadata.findStart = Find(searchText, 0, SelectionStart, RichTextBoxFinds.WholeWord | RichTextBoxFinds.Reverse);
}
else if (isReverse && isMatchCase && !isWholeWord)
{
objFindMetadata.findStart = Find(searchText, 0, SelectionStart, RichTextBoxFinds.MatchCase | RichTextBoxFinds.Reverse);
}
else
{
objFindMetadata.findStart = Find(searchText, 0, SelectionStart, RichTextBoxFinds.MatchCase | RichTextBoxFinds.WholeWord | RichTextBoxFinds.Reverse);
}
found = false;
if (objFindMetadata.findStart >= 0)
{
if (!isReverse)
{
if (saveSelectionStart <= objFindMetadata.findStart)
{
found = true;
}
}
else
{
if (SelectionStart < saveSelectionStart)
{
found = true;
}
}
}
if (found)
{
if (previousSaveFindIndex != -1)
{
Select(objFindMetadata.highLightStart, objFindMetadata.highLightLength);
objFindMetadata.ClearHighLight();
SelectionBackColor = Color.White;
}
objFindMetadata.highLightStart = objFindMetadata.findStart;
objFindMetadata.highLightLength = objFindMetadata.findLength = searchText.Length;
Select(objFindMetadata.findStart, objFindMetadata.findLength);
SelectionBackColor = Color.Yellow;
SelectionLength = 0;
}
else
{
objFindMetadata.ClearFind();
SelectionLength = 0;
SelectionStart = saveSelectionStart;
}
ControlExtensions.Resume(this);
objRichTextBoxSelectionArgs.Set(SelectionStart);
OnSelectionDataIsInteresting(objRichTextBoxSelectionArgs);
SelectionChanged += ClsRichTextBox_SelectionChanged;
Focus();
return found;
}
protected virtual void OnSelectionDataIsInteresting(ClsRichTextBoxSelectionArgs e)
{
SelectionDataIsInteresting?.Invoke(this, e);
}
public event EventHandler<ClsRichTextBoxSelectionArgs> SelectionDataIsInteresting;
private class ClsFindMetadata
{
internal int findStart = -1;
internal int findLength = -1;
internal int highLightStart = -1;
internal int highLightLength = -1;
internal void ClearFind()
{
findStart = -1;
findLength = -1;
}
internal void ClearHighLight()
{
highLightStart = -1;
highLightLength = -1;
}
}
}
public class ClsRichTextBoxSelectionArgs : EventArgs
{
internal void Set(int selectionStart)
{
SelectionStart = selectionStart;
}
public int SelectionStart { get; set; }
}
}