Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to find text in a richtextbox going reverse?

I'm trying to make a search code that allows you to find text in both directions (right and left). I've already made a code that searches text to the right but, not to the left.

Example of what I'm trying to do:

example1 example2 example3 example4

Lets say I found 'example3' (going to the right) and now I need to find example2 (going to the left) How will i make that code?

Question: How to make a code that searches text going to the left (reverse)?

Here is an example of what I've tried to do for searching text to the left:

try
{
    richTextBox1.Focus();
    richTextBox1.Find(findwhat, findPos, RichTextBoxFinds.Reverse);
    richTextBox1.Select(findPos, findwhat.Length);
    findPos += findwhat.Length;
}
catch
{
    findPos = 0;
}
like image 329
David Fields Avatar asked Oct 20 '25 08:10

David Fields


2 Answers

To search forward use this code:

int p;
p = RichTextBox1.Find(textToFind, RichTextBox1.SelectionStart + 1, RichTextBoxFinds.None);
if (p == -1)
    Interaction.MsgBox("Search text was not found.");

To search backward use this code:

int p;
p = RichTextBox1.Find(textToFind, 0, RichTextBox1.SelectionStart, RichTextBoxFinds.Reverse);
if (p == -1)
    Interaction.MsgBox("Search text was not found.");
like image 98
and his dog Avatar answered Oct 22 '25 04:10

and his dog


I had the same need. Here is a working WinForms app to demonstrate. Solving this was inspired by the thread "RichTextBox reverse find" on CodeGuru.

In my demo, the RTB is preloaded with text and is read-only, i.e., I didn't try to solve the issues associated with implementing find forward and reverse with an editable RTB.

demo program screen shot

The form has these controls:

  • Forms.Button named objButtonFind. Text = "Find"
  • Forms.TextBox named objTextBoxSearchWord
  • Forms.Checkbox named objReverse. Text = "Reverse"
  • Forms.Checkbox named objCheckBoxMatchCase. Text = "Match case"
  • Forms.Checkbox named objCheckBoxWholeWord. Text = "Whole word"
  • Forms.Lable named label1. Text = "RTB Start Pos"
  • Forms.TextBox named objTextBoxStartPos.
  • Class ClsRichTextBox derived from Forms.RichTextBox

ClsProgram.cs

using System;
using System.Windows.Forms;

namespace RichTextBoxFindWithReverse
{
    static class ClsProgram
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new ClsFormMain());
        }
    }

    /// <summary>
    /// https://stackoverflow.com/questions/1550293/stopping-textbox-flicker-during-update
    /// </summary>
    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 //(objCheckBoxMatchCase.Checked && objCheckBoxWholeWord.Checked)
            {
                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);
            }
        }

        /// <summary>
        /// Display rich text box selection data
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        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();
        }

        /// <summary>
        /// Clear the find data and highlighting (yellow background) if the user clicks on the text in the control
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ClsRichTextBox_SelectionChanged(object sender, EventArgs e)
        {
            ClearLastFind();

            objRichTextBoxSelectionArgs.Set(SelectionStart);
            OnSelectionDataIsInteresting(objRichTextBoxSelectionArgs);
        }

        /// <summary>
        /// If last find data is available (findIndex, findLength), clear the highlighting 
        /// </summary>
        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;
        }

        // -----------------------------------------------------------------------
        // -----------------------------------------------------------------------
        // -----------------------------------------------------------------------

        /// <summary>
        /// If searchText is found, returns true. Otherwise, returns false
        /// </summary>
        /// <param name="searchText"></param>
        /// <param name="isReverse"></param>
        /// <param name="isMatchCase"></param>
        /// <param name="isWholeWord"></param>
        /// <returns></returns>
        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 // (isReverse && isMatchCase && isWholeWord)
            {
                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)
            {
                // ClearLastFind isn't applicable because it clears find metadata. Just clear the highlight
                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;
        }

        /// <summary>
        /// Method used to invoke the event that is used to report RTB SelectionStart to interested parties
        /// https://learn.microsoft.com/en-us/dotnet/api/system.eventhandler-1?view=netframework-4.7.2
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnSelectionDataIsInteresting(ClsRichTextBoxSelectionArgs e)
        {
            SelectionDataIsInteresting?.Invoke(this, e);
        }

        /// <summary>
        /// Event used to report RTB SelectionStart to interested parties
        /// </summary>
        public event EventHandler<ClsRichTextBoxSelectionArgs> SelectionDataIsInteresting;

        /// <summary>
        /// Class used to record state of find results and find highlighting
        /// </summary>
        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;
            }
        }
    }

    /// <summary>
    /// Class used to report RTB SelectionStart to interested parties
    /// </summary>
    public class ClsRichTextBoxSelectionArgs : EventArgs
    {
        internal void Set(int selectionStart)
        {
            SelectionStart = selectionStart;
        }

        public int SelectionStart { get; set; }
    }
}
like image 28
VA systems engineer Avatar answered Oct 22 '25 04:10

VA systems engineer



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!