Related
I'm using ScintillaNET in VisualStudio/C#.
When the user clicks (LMB or RMB) a specific word inside the text, I need to get the surrounding symbols. For example:
This is <a test> to show my <problem>
In this case, if the user clicks over the word "test", I want to retrieve the entire block between "<" and ">", so I need to get <a test>.
If the user clicks over "problem" I need to get <problem>.
I know that I can get the caret position then "navigate" (for loop) before the position (going left) to find the first occurence of "<", then "navigate" after the caret position (going right) to find the first occurrence of ">".
But is there any other better way to achieve this? Does Scintilla supply some methods to find them?
Thank you for your help!
I used this code to find a workaround, but frankly speaking I wish to find a better solution:
const char CHAR_START_BLOCK = '[';
const char CHAR_END_BLOCK = ']';
if(e.Button == MouseButtons.Right) {
int leftPos = -1;
int rightPos = -1;
//
// Search for CHAR_START_BLOCK
//
for(int i = scintilla1.CurrentPosition; i >= 1; i--) {
if(scintilla1.GetCharAt(i) == CHAR_START_BLOCK) {
leftPos = i;
break;
}
if(scintilla1.GetCharAt(i) == '\r') {
break;
}
if( (scintilla1.GetCharAt(i) == CHAR_END_BLOCK) && (i != scintilla1.CurrentPosition)) {
break;
}
}
if(leftPos != -1) {
//
// Search for CHAR_END_BLOCK
//
string currentLine = scintilla1.Lines[scintilla1.CurrentLine].Text;
for(int i = scintilla1.CurrentPosition; i <= (scintilla1.CurrentPosition + currentLine.len()); i++) {
if(scintilla1.GetCharAt(i) == CHAR_END_BLOCK) {
rightPos = i;
break;
}
}
LogManager.addLog("LEFT/RIGHT: " + scintilla1.GetTextRange(leftPos, (rightPos + 1 - leftPos)));
}
}
I am using C# WinForm, and I have a RichTextBox that I am trying to make look like a C# script.
Means when using specific words, I want them to be colored. When they edit the word by changing it, I want it to go back to be black.
My approach works, but it really messy and cause bugs when the a scroll option is created and needed to be used to see the code below. (When typing, pretty much the richtextbox jumps up and down without stop)
private void ScriptRichTextBox_TextChanged(object sender, EventArgs e)
{
ScriptTextChange = ScriptRichTextBox.Text;
ScriptColorChange();
}
private void ScriptColorChange()
{
int index = ScriptRichTextBox.SelectionStart;
ScriptRichTextBox.Text = ScriptTextChange; //Only way I found to make the all current text black again, SelectAll() didn't work well.
ScriptRichTextBox.SelectionStart = index;
String[] coloredNames = {"Main", "ClickMouseDown", "ClickMouseUp", "PressKey", "StopMoving", "Delay", "GoRight", "GoLeft", "GoUp", "GoDown", "MousePosition", "LastHorizontalDirection", "LastVerticalDirections", "CurrentDirection", "Directions" };
String[] coloredNames2 = { "cm.", "if", "else", "while", "switch", "case", "break", "return", "new" };
String[] coloredNames3 = { "MyPosition", "MyHp", "MyMp", "OtherPeopleInMap", ".RIGHT", ".LEFT", ".UP", ".DOWN", ".STOP_MOVING" };
foreach (String s in coloredNames)
this.CheckKeyword(s, Color.LightSkyBlue, 0);
foreach (String s in coloredNames2)
this.CheckKeyword(s, Color.Blue, 0);
foreach (String s in coloredNames3)
this.CheckKeyword(s, Color.DarkGreen, 0);
}
private void CheckKeyword(string word, Color color, int startIndex)
{
if (this.ScriptRichTextBox.Text.Contains(word))
{
int index = 0;
int selectStart = this.ScriptRichTextBox.SelectionStart;
while ((index = this.ScriptRichTextBox.Text.IndexOf(word, (index + 1))) != -1)
{
this.ScriptRichTextBox.Select((index + startIndex), word.Length);
this.ScriptRichTextBox.SelectionColor = color;
this.ScriptRichTextBox.Select(selectStart, 0);
this.ScriptRichTextBox.SelectionColor = Color.Black;
}
}
}
I refactored your code a little to hopefully demonstrate a better approach to colouring the text. It is also not optimal to instantiate your string arrays every time you fire the TextChanged event.
Updated:The idea is to build up a word buffer that will be matched with your set of words when typing.
The buffer records each key and if it .IsLetterOrDigit it adds it to the StringBuilder buffer. The buffer has some additional bugs, with recording key press values and not removing recorded chars if you hit backspace etc..
Instead of the word buffer, use RegEx to match any of the words in your reserve word list. Build up the reserve word RegEx so you end up with something like \b(word|word2|word3....)\b This is done in the code in the BuildRegExPattern(..) method.
Once you hit any key other than a letter or number the buffer is checked for content and if the content matches a word then only the text right before the cursor in the ScriptRichTextBox.Text is checked and changed.
Remove the .(dots) from the reserve words as this just complicates the matching criteria. The RegEx in the built up patters will match the words exactly, so if you type something like FARRIGHT or cms the words will not partially change colour.
As an extra I also covered the paste process pressing Ctrl+V because it is a bit of a pain in WinForms and will probably happen quite often.
There are older questions eg. this one that cover the scrolling behaviour, where it shows how to interop by adding the [System.Runtime.InteropServices.DllImport("user32.dll")] attribute, but it can be done without it.
To prevent all the scroll jumping you can make use of the .DefWndProc(msg) method on the form. this question pointed me towards the WM_SETREDRAW property.
There is also this list of other properties that can be set.
The full implementation is this:
public partial class Form1 : Form
{
private readonly string[] _skyBlueStrings;
private readonly string[] _blueStrings;
private readonly string[] _greenStrings;
//for pasting
bool _IsCtrl;
bool _IsV;
//value to fix the colour not setting first character after return key pressed
int _returnIdxFix = 0;
//regex patterns to use
string _LightBlueRegX = "";
string _BlueRegX = "";
string _GreenRegX = "";
//match only words
Regex _rgxAnyWords = new Regex(#"(\w+)");
//colour setup
Color _LightBlueColour = Color.LightSkyBlue;
Color _BlueColour = Color.Blue;
Color _GreenColour = Color.DarkGreen;
Color _DefaultColour = Color.Black;
public Form1()
{
InitializeComponent();
_skyBlueStrings = new string[] { "Main", "ClickMouseDown", "ClickMouseUp", "PressKey", "StopMoving", "Delay", "GoRight", "GoLeft", "GoUp", "GoDown", "MousePosition", "LastHorizontalDirection", "LastVerticalDirections", "CurrentDirection", "Directions" };
_blueStrings = new string[] { "cm", "if", "else", "while", "switch", "case", "break", "return", "new" };
_greenStrings = new string[] { "MyPosition", "MyHp", "MyMp", "OtherPeopleInMap", "RIGHT", "LEFT", "UP", "DOWN", "STOP_MOVING" };
_LightBlueRegX = BuildRegExPattern(_skyBlueStrings);
_BlueRegX = BuildRegExPattern(_blueStrings);
_GreenRegX = BuildRegExPattern(_greenStrings);
}
string BuildRegExPattern(string[] keyworkArray)
{
StringBuilder _regExPatern = new StringBuilder();
_regExPatern.Append(#"\b(");//beginning of word
_regExPatern.Append(string.Join("|", keyworkArray));//all reserve words
_regExPatern.Append(#")\b");//end of word
return _regExPatern.ToString();
}
private void ProcessAllText()
{
BeginRtbUpdate();
FormatKeywords(_LightBlueRegX, _LightBlueColour);
FormatKeywords(_BlueRegX, _BlueColour);
FormatKeywords(_GreenRegX, _GreenColour);
//internal function to process words and set their colours
void FormatKeywords(string regExPattern, Color wordColour)
{
var matchStrings = Regex.Matches(ScriptRichTextBox.Text, regExPattern);
foreach (Match match in matchStrings)
{
FormatKeyword(keyword: match.Value, wordIndex: match.Index, wordColour: wordColour);
}
}
EndRtbUpdate();
ScriptRichTextBox.Select(ScriptRichTextBox.Text.Length, 0);
ScriptRichTextBox.Invalidate();
}
void ProcessWordAtIndex(string fullText, int cursorIdx)
{
MatchCollection anyWordMatches = _rgxAnyWords.Matches(fullText);
if (anyWordMatches.Count == 0)
{ return; } // no words found
var allWords = anyWordMatches.OfType<Match>().ToList();
//get the word just before cursor
var wordAtCursor = allWords.FirstOrDefault(w => (cursorIdx - _returnIdxFix) == (w.Index + w.Length));
if (wordAtCursor is null || string.IsNullOrWhiteSpace(wordAtCursor.Value))
{ return; }//no word at cursor or the match was blank
Color wordColour = CalculateWordColour(wordAtCursor.Value);
FormatKeyword(wordAtCursor.Value, wordAtCursor.Index, wordColour);
}
private Color CalculateWordColour(string word)
{
if (_skyBlueStrings.Contains(word))
{ return _LightBlueColour; }
if (_blueStrings.Contains(word))
{ return _BlueColour; }
if (_greenStrings.Contains(word))
{ return _GreenColour; }
return _DefaultColour;
}
private void FormatKeyword(string keyword, int wordIndex, Color wordColour)
{
ScriptRichTextBox.Select((wordIndex - _returnIdxFix), keyword.Length);
ScriptRichTextBox.SelectionColor = wordColour;
ScriptRichTextBox.Select(wordIndex + keyword.Length, 0);
ScriptRichTextBox.SelectionColor = _DefaultColour;
}
#region RichTextBox BeginUpdate and EndUpdate Methods
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
//wait until the rtb is visible, otherwise you get some weird behaviour.
if (ScriptRichTextBox.Visible && ScriptRichTextBox.IsHandleCreated)
{
if (m.LParam == ScriptRichTextBox.Handle)
{
rtBox_lParam = m.LParam;
rtBox_wParam = m.WParam;
}
}
}
IntPtr rtBox_wParam = IntPtr.Zero;
IntPtr rtBox_lParam = IntPtr.Zero;
const int WM_SETREDRAW = 0x0b;
const int EM_HIDESELECTION = 0x43f;
void BeginRtbUpdate()
{
Message msg_WM_SETREDRAW = Message.Create(ScriptRichTextBox.Handle, WM_SETREDRAW, (IntPtr)0, rtBox_lParam);
this.DefWndProc(ref msg_WM_SETREDRAW);
}
public void EndRtbUpdate()
{
Message msg_WM_SETREDRAW = Message.Create(ScriptRichTextBox.Handle, WM_SETREDRAW, rtBox_wParam, rtBox_lParam);
this.DefWndProc(ref msg_WM_SETREDRAW);
//redraw the RichTextBox
ScriptRichTextBox.Invalidate();
}
#endregion
private void ScriptRichTextBox_TextChanged(object sender, EventArgs e)
{
//only run all text if it was pasted NOT ON EVERY TEXT CHANGE!
if (_IsCtrl && _IsV)
{
_IsCtrl = false;
ProcessAllText();
}
}
protected void ScriptRichTextBox_KeyPress(object sender, KeyPressEventArgs e)
{
if (!char.IsLetterOrDigit(e.KeyChar))
{
//if the key was enter the cursor position is 1 position off
_returnIdxFix = (e.KeyChar == '\r') ? 1 : 0;
ProcessWordAtIndex(ScriptRichTextBox.Text, ScriptRichTextBox.SelectionStart);
}
}
private void ScriptRichTextBox_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
{
if (e.KeyCode == Keys.ControlKey)
{
_IsCtrl = true;
}
if (e.KeyCode == Keys.V)
{
_IsV = true;
}
}
private void ScriptRichTextBox_KeyUp(object sender, System.Windows.Forms.KeyEventArgs e)
{
if (e.KeyCode == Keys.ControlKey)
{
_IsCtrl = false;
}
if (e.KeyCode == Keys.V)
{
_IsV = false;
}
}
}
It looks like this when you paste some "code" with keywords:
and typing looks like this:
Ok after 2 days of not finding something that actually works good or has annoying bugs. I managed to find a solution myself after a big struggle of trying to make it work. The big idea is people try to edit all the RichTextBox words at once, which cause bugs. Why to edit all of the rich text box when you can do your checks on the current word only to get the same result. Which is what I did, I checked if any of my array strings is in the current word, and colored all of them.
private void ScriptRichTextBox_TextChanged(object sender, EventArgs e)
{
FindStringsInCurrentWord();
}
private void FindStringsInCurrentWord()
{
RichTextBox script = ScriptRichTextBox;
String finalWord, forwards, backwards;
int saveLastSelectionStart = script.SelectionStart;
int index = script.SelectionStart;
String[] coloredNames = { "Main", "ClickMouseDown", "ClickMouseUp", "PressKey", "StopMoving", "Delay", "GoRight", "GoLeft", "GoUp", "GoDown", "MousePosition", "LastHorizontalDirection", "LastVerticalDirections", "CurrentDirection", "Directions" };
String[] coloredNames2 = { "cm.", "if", "else", "while", "switch", "case", "break", "return", "new" };
String[] coloredNames3 = { "MyPosition", "MyHp", "MyMp", "OtherPeopleInMap", ".RIGHT", ".LEFT", ".UP", ".DOWN", ".STOP_MOVING" };
String[] arr2 = coloredNames.Union(coloredNames2).ToArray();
Array arrAll = arr2.Union(coloredNames3).ToArray(); //Gets all arrays together
Array[] wordsArray = { coloredNames, coloredNames2, coloredNames3 }; //All found strings in the word
List<String> wordsFoundList = new List<String>();
int foundChangedColor = 0;
int wordsFound = 0;
char current = (char)script.GetCharFromPosition(script.GetPositionFromCharIndex(index)); //Where the editor thingy is
//Check forward text where he uses space and save text
while (!System.Char.IsWhiteSpace(current) && index < script.Text.Length)
{
index++;
current = (char)script.GetCharFromPosition(script.GetPositionFromCharIndex(index));
}
int lengthForward = index - saveLastSelectionStart;
script.Select(script.SelectionStart, lengthForward);
forwards = script.SelectedText;
//Debug.WriteLine("Forwards: " + forwards);
script.SelectionStart = saveLastSelectionStart;
this.ScriptRichTextBox.Select(script.SelectionStart, 0);
index = script.SelectionStart;
current = (char)script.GetCharFromPosition(script.GetPositionFromCharIndex(index));
int length = 0;
//Check backwords where he uses space and save text
while ((!System.Char.IsWhiteSpace(current) || length == 0) && index > 0 && index <= script.Text.Length)
{
index--;
length++;
current = (char)script.GetCharFromPosition(script.GetPositionFromCharIndex(index));
}
script.SelectionStart -= length;
script.Select(script.SelectionStart + 1, length - 1);
backwards = script.SelectedText;
//Debug.WriteLine("Backwards: " + backwards);
script.SelectionStart = saveLastSelectionStart;
this.ScriptRichTextBox.Select(saveLastSelectionStart, 0);
this.ScriptRichTextBox.SelectionColor = Color.Black;
finalWord = backwards + forwards; //Our all word!
//Debug.WriteLine("WORD:" + finalWord);
//Setting all of the word black, after it coloring the right places
script.Select(index + 1, length + lengthForward);
script.SelectionColor = Color.Black;
foreach (string word in arrAll)
{
if (finalWord.IndexOf(word) != -1)
{
wordsFound++;
wordsFoundList.Add(word);
script.Select(index + 1 + finalWord.IndexOf(word), word.Length);
if (coloredNames.Any(word.Contains))
{
script.SelectionColor = Color.LightSkyBlue;
foundChangedColor++;
}
else if (coloredNames2.Any(word.Contains))
{
script.SelectionColor = Color.Blue;
foundChangedColor++;
}
else if (coloredNames3.Any(word.Contains))
{
script.SelectionColor = Color.DarkGreen;
foundChangedColor++;
}
//Debug.WriteLine("Word to edit: " + script.SelectedText);
this.ScriptRichTextBox.Select(saveLastSelectionStart, 0);
this.ScriptRichTextBox.SelectionColor = Color.Black;
}
}
//No strings found, color it black
if (wordsFound == 0)
{
script.Select(index + 1, length + lengthForward);
script.SelectionColor = Color.Black;
//Debug.WriteLine("WORD??: " + script.SelectedText);
this.ScriptRichTextBox.Select(saveLastSelectionStart, 0);
this.ScriptRichTextBox.SelectionColor = Color.Black;
}
}
fileName = txtBxFileNamePath.Text;
if (File.Exists(fileName))
{
if (txtBxDate.Text != null && txtBxNumber.Text != null && txtBxUnit.Text != null && txtBxUnitPrice.Text != null && txtBxShipTo.Text != null
&& txtBxOrdered.Text != null && richTxBxDesc.Text != null)
{
try
{
int higherThanZero = Int32.Parse(txtBxNumber.Text);
if (higherThanZero > 0)
{
using (StreamReader reader = File.OpenText(fileName))
{
string[] lines = File.ReadAllLines(fileName);
for (int i = 0; i < lines.Length - 1; i++)
{
string firstNum = lines[i].Substring(0, 2);
if (firstNum == txtBxNumber.Text)
{
string record = "hello ";
lines[i].Replace(lines[i], record);
}
else
{
int orderNum = Int32.Parse(txtBxOrdered.Text);
int unitPriceNum = Int32.Parse(txtBxUnitPrice.Text);
double tax = .13;
int taxInt = (int)tax;
int amount = orderNum * unitPriceNum;
string amountStr = amount.ToString();
int amountTotal = amount * taxInt;
string amountTotalStr = amountTotal.ToString();
amountList.Add(amountStr);
amountTotalList.Add(amountTotalStr);
string record = amountTotalStr.PadRight(30) + amountStr.PadRight(30);
richTxtBxRecord.Text += record + "\n";
using (StreamWriter write = new StreamWriter(fileName, true))
{
write.WriteLine(record + "\n");
write.Close();
}
}
}
}
}
else
{
richTxtBxError.Text += "Textbox Number must contain a digit higher than 0 ";
}
}
catch
{
richTxtBxError.Text += "Please make sure number text box is a digit";
}
}
else
{
richTxtBxError.Text += "please make sure that no text boxes are empty";
}
}
else
{
richTxtBxError.Text += "Please select a file that already exists";
}
I am having an issue where once i get past the try-catch statement "please make sure number is a digit, no code executes. I am trying to obtain the first few characters in a text file and match it with the users input. If the input is the same as what is already inserted in the text file, i update the whole record. If there is no match (non existent number) i write in a brand new record.
I can't quite follow your logic, but I tried. You should be able to take this code and do what you want (whatever it is).
I started by declaring some class level variables.
private DateTime _dateValue;
private int _numberValue;
private decimal _unitPrice;
private int _numberOrdered;
Then, since you have so many preconditions and so many text boxes, I factored out the validation and setting of these variables. It makes the logic (whatever it supposed to be) much easier to follow:
private bool ValidateUserEntry()
{
bool isError = false;
if (!File.Exists(txtBxFileNamePath.Text))
{
AddError("File Name must exist");
isError = true;
}
if (txtBxDate.Text == string.Empty || !DateTime.TryParse(txtBxDate.Text, out var _dateValue))
{
AddError("The date must be a valid date");
isError = true;
}
if (txtBxNumber.Text == string.Empty || !int.TryParse(txtBxNumber.Text, out _numberValue) ||
_numberValue <= 0)
{
AddError("You must enter a number greater than 0 for [Number]");
isError = true;
}
if (txtBxUnitPrice.Text == string.Empty || !decimal.TryParse(txtBxUnitPrice.Text, out _unitPrice) ||
_unitPrice <= 0.0m)
{
AddError("The unit price must be a positive decimal number");
isError = true;
}
if (txtBxShipTo.Text == string.Empty)
{
AddError("A ship to address is required");
isError = true;
}
if (txtBxOrdered.Text == string.Empty || !int.TryParse(txtBxOrdered.Text, out _numberOrdered) ||
_numberOrdered <= 0)
{
AddError("The Number ordered must be a number greater than 0");
isError = true;
}
if (richTxBxDesc.Text == string.Empty)
{
AddError("A description is required");
isError = true;
}
return !isError;
}
I also added two utility functions for managing the error list:
private void ClearError()
{
richTxtBxError.Text = string.Empty;
}
private void AddError(string errorMessage)
{
richTxtBxError.Text += (errorMessage + Environment.NewLine);
richTxtBxError.SelectionStart = richTxtBxError.Text.Length;
richTxtBxError.SelectionLength = 0;
}
Now comes the real code. Near as I can tell, you want to scan a text file. If the number in the first few character positions matches a number in your input, then you change the line to some constant text. Otherwise, you want to do a calculation and put the results of the calculation on the line of text.
My input file looks like this:
1 First
2 Second
3 Third
12 Twelth
13 Thirteenth
34 Thirty-fourth
and the code that I run looks like what's below. The logic makes no sense, but it was what I could discern from your code. Instead of trying to do things on the fly to a file (which never really turns out well unless you are really careful), I gather the output into a List<string>. Once I have all the output, I put it in a text box control and overwrite the file.
ClearError();
//check pre-conditions
if (!ValidateUserEntry())
{
return;
}
string[] lines;
using (StreamReader reader = File.OpenText(txtBxFileNamePath.Text))
{
lines = File.ReadAllLines(txtBxFileNamePath.Text);
}
List<string> newLines = new List<string>();
for (var lineIndex = 0; lineIndex < lines.Length; ++lineIndex)
{
var line = lines[lineIndex];
if (line.Length > 2 && int.TryParse(line.Substring(0, 2), out var linePrefixNumber) &&
linePrefixNumber == _numberValue)
{
newLines.Add("Bingo, hit the right record");
}
else
{
decimal tax = .13m;
var amount = _numberOrdered * _unitPrice;
var amountTotal = amount * (1m + tax);
//amountList.Add(amount.TosString());
//amountTotalList.Add(amountTotal.ToString());
var newRecord = $"{amountTotal,30:C}{amount,30:C}";
newLines.Add(newRecord); //every record but one will be the same, but, such is life
}
}
//at this point, the newLines list has what I want
//put it in the text box
richTxtBxRecord.Text = string.Join(Environment.NewLine, newLines);
//and write it out
using (StreamWriter write = new StreamWriter(txtBxFileNamePath.Text, append:false))
{
write.Write(richTxtBxRecord.Text);
write.Flush();
}
With inputs that look like:
Number: 12
Number Ordered: 3
Unit Price: 1.23
The output (oddly enough - but it's what I could figure from your code) looks like:
$4.17 $3.69
$4.17 $3.69
$4.17 $3.69
Bingo, hit the right record
$4.17 $3.69
$4.17 $3.69
You can see that the input line that had the 12 at the start gets switched for bingo. The rest get the same information. I'm sure that's not what you want. But, with this code, you should be able to get something that you'd like.
Also note that I treat all the currency values as decimal (not int or double). For the life of me, I have no idea what you were trying to do with the taxInt variable (it will always be zero the way you have coded it). Instead, I did a rational tax calculation.
All of the code below the catch block is inside an else block, so I wouldn't expect it to execute. If you want something to execute after the catch, remove it from the else block.
In my RichtextBox, if I have written as below.
This is my pen,
his pen is beautiful.
Now I search word "is" then
output would be as below.
All "is" should be highlighted.
What about:
static class Utility {
public static void HighlightText(this RichTextBox myRtb, string word, Color color) {
if (word == string.Empty)
return;
int s_start = myRtb.SelectionStart, startIndex = 0, index;
while((index = myRtb.Text.IndexOf(word, startIndex)) != -1) {
myRtb.Select(index, word.Length);
myRtb.SelectionColor = color;
startIndex = index + word.Length;
}
myRtb.SelectionStart = s_start;
myRtb.SelectionLength = 0;
myRtb.SelectionColor = Color.Black;
}
}
Looks like this would do it.
http://www.dotnetcurry.com/ShowArticle.aspx?ID=146
int start = 0;
int indexOfSearchText = 0;
private void btnFind_Click(object sender, EventArgs e)
{
int startindex = 0;
if(txtSearch.Text.Length > 0)
startindex = FindMyText(txtSearch.Text.Trim(), start, rtb.Text.Length);
// If string was found in the RichTextBox, highlight it
if (startindex >= 0)
{
// Set the highlight color as red
rtb.SelectionColor = Color.Red;
// Find the end index. End Index = number of characters in textbox
int endindex = txtSearch.Text.Length;
// Highlight the search string
rtb.Select(startindex, endindex);
// mark the start position after the position of
// last search string
start = startindex + endindex;
}
}
public int FindMyText(string txtToSearch, int searchStart, int searchEnd)
{
// Unselect the previously searched string
if (searchStart > 0 && searchEnd > 0 && indexOfSearchText >= 0)
{
rtb.Undo();
}
// Set the return value to -1 by default.
int retVal = -1;
// A valid starting index should be specified.
// if indexOfSearchText = -1, the end of search
if (searchStart >= 0 && indexOfSearchText >=0)
{
// A valid ending index
if (searchEnd > searchStart || searchEnd == -1)
{
// Find the position of search string in RichTextBox
indexOfSearchText = rtb.Find(txtToSearch, searchStart, searchEnd, RichTextBoxFinds.None);
// Determine whether the text was found in richTextBox1.
if (indexOfSearchText != -1)
{
// Return the index to the specified search text.
retVal = indexOfSearchText;
}
}
}
return retVal;
}
// Reset the richtextbox when user changes the search string
private void textBox1_TextChanged(object sender, EventArgs e)
{
start = 0;
indexOfSearchText = 0;
}
This will show all the searched criteria at the same time.
Using: 1 Textbox (to enter the text to search for) and 1 Button (to Run the Search).
Enter your search criteria inside the textbox and press search button.
// On Search Button Click: RichTextBox ("rtb") will display all the words inside the document
private void btn_Search_Click(object sender, EventArgs e)
{
try
{
if (rtb.Text != string.Empty)
{// if the ritchtextbox is not empty; highlight the search criteria
int index = 0;
String temp = rtb.Text;
rtb.Text = "";
rtb.Text = temp;
while (index < rtb.Text.LastIndexOf(txt_Search.Text))
{
rtb.Find(txt_Search.Text, index, rtb.TextLength, RichTextBoxFinds.None);
rtb.SelectionBackColor = Color.Yellow;
index = rtb.Text.IndexOf(txt_Search.Text, index) + 1;
rtb.Select();
}
}
}
catch (Exception ex) { MessageBox.Show(ex.Message, "Error"); }
}
}
}
If you only want to match the whole word you can use this, note that this ignores case and also the |s\b means that plurals get highlighted e.g. Cat matches cats but not caterpiller :
public static void HighlightText(RichTextBox myRtb, string word, Color color)
{
if (word == string.Empty)
return;
var reg = new Regex(#"\b" + word + #"(\b|s\b)",RegexOptions.IgnoreCase);
foreach (Match match in reg.Matches(myRtb.Text))
{
myRtb.Select(match.Index, match.Length);
myRtb.SelectionColor = color;
}
myRtb.SelectionLength = 0;
myRtb.SelectionColor = Color.Black;
}
private void button3_Click(object sender, EventArgs e)
{
if (textBox1.Text != "")
{
for (int i = 0; i < richTextBox1.TextLength; i++)
{
richTextBox1.Find(textBox1.Text, i, RichTextBoxFinds.None);
richTextBox1.SelectionBackColor = Color.Red;
}
}
else
{
for (int i = 0; i < richTextBox1.TextLength; i++)
{
richTextBox1.SelectAll();
richTextBox1.SelectionBackColor = Color.White;
}
}
}[lets make it!][1]
I would do it like that because all the other answers highlight the text, but doesnt change it back after you searched again.
Use the RichText Find Method to find the starting index for the searching word.
public int FindMyText(string searchText, int searchStart, int searchEnd)
{
int returnValue = -1;
if (searchText.Length > 0 && searchStart >= 0)
{
if (searchEnd > searchStart || searchEnd == -1)
{
int indexToText = richTextBox1.Find(searchText, searchStart, searchEnd, RichTextBoxFinds.MatchCase);
if (indexToText >= 0)
{
returnValue = indexToText;
}
}
}
return returnValue;
}
Use a Button or TextChangeListener and Search for your word.
private void button1_Click(object sender, EventArgs e)
{
// Select the first char in your Richtextbox
richTextBox1.SelectionStart = 0;
richTextBox1.SelectionLength = richTextBox1.TextLength;
// Select until the end
richTextBox1.SelectionColor = Color.Black;
// Make the Text Color black
//Use an Inputfield to add the searching word
var word = txtSearch.Text;
//verify the minimum length otherwise it may freeze if you dont have text inside
if (word.Length > 3)
{
int s_start = richTextBox1.SelectionStart, startIndex = 0, index;
while ((index = FindMyText(word, startIndex, richTextBox1.TextLength)) != -1)
{
// goes through all possible found words and color them blue (starting index to end)
richTextBox1.Select(index, word.Length);
richTextBox1.SelectionColor = Color.Blue;
startIndex = index + word.Length;
}
// Color everything between in color black to highlight only found words
richTextBox1.SelectionStart = startIndex;
richTextBox1.SelectionLength = 0;
richTextBox1.SelectionColor = Color.Black;
}
}
I would highly recommend to set a minimum word length to avoid freezing and high memory allocation.
I have a RichTextBox with -by example- this text:
"This is my Text"
Now I want to "search" the RichTextBox for a Text (String), by example:
"Text"
Now "Text" should be selected/highlighted (for each one) in the RichTextBox..
There is something like:
myRichTextBox.Select();
but here I have to set a StartPosition and so on, but I want to search for String!
How could I do this? (Searched stackoverflow, didn't find something similiar..)
int start = 0;
int indexOfSearchText = 0;
private void btnFind_Click(object sender, EventArgs e)
{
int startindex = 0;
if(txtSearch.Text.Length > 0)
startindex = FindMyText(txtSearch.Text.Trim(), start, rtb.Text.Length);
// If string was found in the RichTextBox, highlight it
if (startindex >= 0)
{
// Set the highlight color as red
rtb.SelectionColor = Color.Red;
// Find the end index. End Index = number of characters in textbox
int endindex = txtSearch.Text.Length;
// Highlight the search string
rtb.Select(startindex, endindex);
// mark the start position after the position of
// last search string
start = startindex + endindex;
}
}
public int FindMyText(string txtToSearch, int searchStart, int searchEnd)
{
// Unselect the previously searched string
if (searchStart > 0 && searchEnd > 0 && indexOfSearchText >= 0)
{
rtb.Undo();
}
// Set the return value to -1 by default.
int retVal = -1;
// A valid starting index should be specified.
// if indexOfSearchText = -1, the end of search
if (searchStart >= 0 && indexOfSearchText >=0)
{
// A valid ending index
if (searchEnd > searchStart || searchEnd == -1)
{
// Find the position of search string in RichTextBox
indexOfSearchText = rtb.Find(txtToSearch, searchStart, searchEnd, RichTextBoxFinds.None);
// Determine whether the text was found in richTextBox1.
if (indexOfSearchText != -1)
{
// Return the index to the specified search text.
retVal = indexOfSearchText;
}
}
}
return retVal;
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
start = 0;
indexOfSearchText = 0;
}
CheckOut this article if you dont understand this code...
http://www.dotnetcurry.com/ShowArticle.aspx?ID=146
You can only have one selection in a text box. What you want is to highlight the found text.
You could achieve it like this:
Find the positions of the text you want to highlight using repeated calls to myRichTextBox.Text.IndexOf with the last found index + 1 as the start position.
Highlight the found texts using the default RichTextBox capabilities.
You can use the Find method to find the startindex of your searched text:
int indexToText = myRichTextBox.Find(searchText);
You can then use this index and the Select method to select the text:
int endIndex = searchText.Length;
myRichTextBox.Select(indexToText , endIndex);
private void Txt_Search_Box_TT_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
FOFO:
int start =
RtfAll_Messages.Find(Txt_Search_Box_TT.Text, RtfAll_Messages.SelectionStart + 1,
RichTextBoxFinds.None);
if (start >= 0)
RtfAll_Messages.Select(start, Txt_Search_Box_TT.Text.Length);
else
{
start = 0;
RtfAll_Messages.SelectionStart = 0;
RtfAll_Messages.SelectionLength = 0;
}
}
}