C#: Auto text highlight in RichText - c#

I am extremely very new to C#, just wrote some calculator, text editors and DB client in the school almost 10 years ago :) Not I am trying to make a tool for myself and my colleagues to view traces and logs in easier way. All we know Notepad++, we used daily for text highlighting, styling, but the thing is that these highlights get lost after you close Notepad++.
So my goal now is to make the same text editor but so it will be able to save your work.Currently I am working on the feature so when I am selecting some text, it will search for the same on whole document and highlight it, for example with red background. I added this one:
private void richTextBox1_SelectionChanged(object sender, EventArgs e)
{
int startIndex = 0;
while (startIndex<richTextBox1.TextLength)
{
int wordStartIndex = richTextBox1.Find(richTextBox1.SelectedText, startIndex, RichTextBoxFinds.None);
if (wordStartIndex != -1)
{
richTextBox1.SelectionStart = wordStartIndex;
richTextBox1.SelectionLength = richTextBox1.SelectionLength;
richTextBox1.SelectionBackColor = Color.Red;
}
else
break;
startIndex += wordStartIndex + richTextBox1.SelectionLength;
}
}
But it gives me "StackOverFlow" as I have a loop here. Can you please assist me with it?
I think I need to run 2 searches to avoid loop - one before selection index, one after. Or maybe there is easier option?
Thank you all, guys!

You're getting an infinite loop because you're in an event that checks for a selection change, and then in that event, you're changing the selection, which causes an event, where you change the selection, which causes an event...
If you want to avoid this you'll need a class level variable like
bool inSelectionChangeEvent;
and then change your code to:
private void richTextBox1_SelectionChanged(object sender, EventArgs e)
{
if (!inSelectionChangeEvent){
inSelectionChangeEvent = true;
}
else{
return;
}
...
Also, you're writing your OWN text editor? Err, there may be a simpler solution :)

Your problem is with the selection length. Right here:
richTextBox1.SelectionLength = richTextBox1.SelectionLength;
It does no good to set something equal to itself and I'm guessing this was an accident. When the SelectionLength is 0, startIndex never increases (anything + 0 is anything).
The first thing I'd do is check if richTextBox1.SelectionLength < 1 and if it is, just return from the method without doing anything.
This code snippet from MSDN should help you accomplish what you're doing:
string wordToFind = "melp";
int index = richTextBox1.Text.IndexOf( wordToFind );
while( index != -1 )
{
richTextBox1.Select( index, wordToFind.Length );
richTextBox1.SelectionColor = Color.Red;
index = richTextBox1.Text.IndexOf( wordToFind, index + wordToFind.Length );
}

Related

How to use functions step by step

Sorry for the strange title, I just don't know how to name this question.
So I have such a function say().
void say(string printedText) {
gameText.text = printedText;
}
And I need to use it several times. Something like this:
say("test text 1");
say("test text 2");
say("test text 3");
...
I need to change text by clicking Space button. Of course I need to use something like this:
if(Input.GetKeyDown(KeyCode.Space)) {
...
}
But I can't understand how to show text step by step. So for example if I click Space button once I should see "test text 1". Next click should show me "test text 2" etc.
How can I realise it? Thanks in advance.
Depending on your needs you could store different texts in a List<string> or maybe even Queue<string> and do
List example
// Add your texts in the editor or by calling texts.Add(someNewString)
public List<string> texts = new List<string>();
private int index = 0;
if(Input.GetKeyDown(KeyCode.Space))
{
// have a safety check if the counter is still within
// valid index values
if(texts.Count > index) say(texts[index]);
// increase index by 1
index++;
}
Array example
Basically the same as the List<string> but you can't add or remove elements "on the fly" (at least not that simple)
public string[] texts;
private int index = 0;
if(Input.GetKeyDown(KeyCode.Space))
{
// have a safety check if the counter is still within
// valid index values
if(texts.Length > index) say(texts[index]);
// increase index by 1
index++;
}
Queue example
public Queue<string> texts = new Queue<string>();
for adding a new text to the end of the queue do
texts.Enqueue(someNewString);
and then
if(Input.GetKeyDown(KeyCode.Space))
{
// retrieves the first entry in the queue and at the same time
// removes it from the queue
if(texts.Count > 0) say(texts.Dequeue());
}
Simple counter
If it is really just about having a different int value then yes simply use a field
private int index;
if(Input.GetKeyDown(KeyCode.Space))
{
// uses string interpolation to replace {0} by the value of index
say($"test text {0}", index);
// increase index by one
index++;
}
define a class field like this:
int count = 0;
and now everytime space is hit:
if(Input.GetKeyDown(KeyCode.Space)) {
say("test text " + count);
count = count + 1;
}
This Code:
if(Input.GetKeyDown(KeyCode.Space)) {
...
}
only works for Unity, In Visual Studio you'll have to create an Event to whatever object you want to do this, for example, if you want to call a void each time you press spacebar youll have to do this, it is easy: (Image below)
In the propieties window press the bolt icon and after double click on the event you want to create (samples): TextChanged, LocationChnaged, MouseMove, etc...
I will use KeyDown on the TextBox object
image
Now in your code this void should be generated
image
Inside this void i writed the code and this is how it looks:
(put int n = 1 before voids)
private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
if(e.KeyCode == Keys.Space)
{
//int n = 1; must be defined
textBox1.Text = "test text " + n;
n++;
}
}
Now each time you'll press or stay pressed spacebar the textbox will be filled with "test text " and the value will be 1 more each time.

Why doesn't my counter count?

This really suprised me. One of the simplest things doesn't work, making a counter. I'm making a kinda game application in c# and there is also a timer who counts the time. Very simple right? I know how to code this and this is something I did before but I don't understand why it isn't working now.
int i = 0;
i++;
label1.Text = i.ToString();
label1.Text turns in to 1 and nothing else happens. Also tryied this with a timer but it freeezes in 1. I know this post isn't really going to help other people but it is very frustrating.
Why you are always getting 1 in your label1 text?
The reason is very simple, each time you are getting to the first line, i is 0:
// Line 1
int i = 0; // declaring and setting i to 0
// Line 2
i++; // incrementing i to 1
// Line 3
label1.Text = i.ToString(); // displaying i (which is equal to 1)
and then again you are getting to the Line 1 and setting i=0, etc...
I presume you have a UI application (win form, web form etc...)
You already mentioned you have a timer that works fine and a label where you output the incremented i variable.
As already commented in order to see a change in your label you can use a loop as following:
int length = 100; // for example
for (int i = 0; i < length; i++)
{
label1.Text = i.ToString();
}
The output in label1 text will be 0, then 1, then 2 .... and finally 99.
Obviously you won't be able to see all those values except the last one 99 at run-time but you can debug and see how it works.
I presume, what you needed is to have your label text changing each time the timer will tick.
Following a code example how you could implement it:
private int i = 0; // initialized once in this UI class
private void timer1_Tick(object sender, EventArgs e)
{
label1.Text = i.ToString();
i++; // will increment by one each time the timer is ticking
}
Set your timer interval to ~1000 so you could clearly see how your text label increments at run-time.
If you want to increment it you have to add the incrementing logic in a loop like for or while loop:
If you want to use timer to count something please refer to this question : here

Windows Forms RichTextBox - Event driven by the word clicked on

I am building a c# win forms application. The application reads an IRC channel and displays the messages that are going through. These messages are displayed like:
{username}: {message that posted or action performed}
I need it so that the user of an application can click on a username (these are stored in array and so can be referenced) another modal form opens with the username passed in. The trouble is, I have no idea how to detect which word in the RichTextBox was clicked on (or even if that is possible).
Any help will be greatly appreciated. I really am at a dead end and other than code that detects a highlighted selection I am no where.
Regards,
Chris
The only solution I could find is to use the RichTextBox method GetCharIndexFromPosition and then perform a loop outward from there, stopping at each end for anything non-alphabetic.
private void richTextBox1_MouseClick(object sender, MouseEventArgs e)
{
int index = richTextBox1.GetCharIndexFromPosition(e.Location);
String toSearch = richTextBox1.Text;
int leftIndex = index;
while (leftIndex < toSearch.Count() && !Char.IsLetter(toSearch[leftIndex]))
leftIndex++; // finds the closest word to the right
if (leftIndex < toSearch.Count()) // did not click into whitespace at the end
{
while (leftIndex > 0 && Char.IsLetter(toSearch[leftIndex - 1]))
leftIndex--;
int rightIndex = index;
while (rightIndex < toSearch.Count() - 1 && Char.IsLetter(toSearch[rightIndex + 1]))
rightIndex++;
String word = toSearch.Substring(leftIndex, rightIndex - leftIndex + 1);
MessageBox.Show(word);
}
}
In your situation, you may have usernames with numbers or spaces and might want to stop the rightIndex when it hits a colon. If the username is always at the start of a newline, you may also want to stop the leftIndex at newlines ('\n').

Accelerate add text to a TextBox

I have code to set text of TexBox as
textBox1.Text = s;
where s is a string that have more than 100,000 char, and it take long time to show text on textBox.
Anybody have solution to make it faster ?
To do that split the s string into many strings, and use the AppendText to add those subStrings, if you check MSDN you will see that :
The AppendText method enables the user to append text to the contents of a text control without using text concatenation, which, can yield better performance when many concatenations are required.
public string s = "Put you terribly long string here";
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
//For responsiveness
textBox1.BeginInvoke(new Action(() =>
{
//Here's your logic
for (int i = 0; i < s.Length; i += 1000)
{
//This if is just for security
if (i+1000 > s.Length)
{
//Here's your AppendText
textBox1.AppendText(s.Substring(i, s.Length-i));
}
else
{
//And it's here as well
textBox1.AppendText(s.Substring(i, 1000));
}
}
}));
}
I used the value 1000, you can use 1500 , 2000 , choose the one that gives better result.
Hope this helps.
Update :
AppendText is available for both WindowsForms and WPF, too bad can't find it on WindowsPhone and WinRT. so I think this solution may help you a lot
break s in sub strings and when you pass first sub string to the text box it will appear after that it concatenate to the second and so on.
other way is that use loop to set the value
for(int i=0;i<s.length; i++)
{
textBox1.Text += s[i];
}
may these helps you

Silverlight C# - How to hold loop progress until an event completes?

I'm trying to loop through the text in a textbox by word in order to spellcheck it. I've split the contents of the textbox into an array, and loop through each word in the array and run it through the spellchecker. When a misspelling is found, I have a popup with a listbox inside it display so that you can choose the correction.
The issue that I'm having, is that it just loops through the whole array and only ends up showing the last correction that needs to be done.
How do I pause the loop so that it waits for a selection to be made and then resume?
Here's the code for the loop:
foreach(string checkedWord in articleWords)
{
bool success = _spellChecker.CheckWord(checkedWord);
List<string> suggest;
if (!success)
{
suggest = _spellChecker.GetSuggestions(checkedWord);
SpellChecklistBox.Items.Clear();
foreach (string s in suggest)
{
SpellChecklistBox.Items.Add(new ListBoxItem() { Content = s });
}
SpellCheckerPopup.IsOpen = true;
SpellChecklistBox.Items.Add(new ListBoxItem() { Content = " ----------------------" });
SpellChecklistBox.Items.Add(new ListBoxItem() { Content = "Ignore" });
}
}
When the SpellCheckerPopup displays, I have an event trigger in the listbox on SelectionChange.
Basically, I need to pause the loop somehow, and then when the SelectionChange event does it's thing, have the loop resume.
Thanks in advance!
-Sootah
If I'm not misunderstanding, currently you are going to:
(1) Check each word in the loop
(2) Pause the loop when an error is found and pop up a suggestion window
(3) User select a suggestion word and resume the loop
I think it's better and easier if the solution is:
(1) Check the word from the first one
(2) Quit the check method with an error flag, and store the position in a variable, pop up a suggestion window
(3) User selects a suggestion word and when User has confirmed the suggestion(e.g. pressing OK on the suggestion window), start the CheckWordMethod again from the stored position
(4) Until step (2) quits with no error flag, which means all words are correct now (but make sure in the whole progress, users can only modify the words by your suggestion window)
#The Smartest: Your answer lead me in the correct direction; actually ended up learning a new datatype out of it! Never used a Queue before. (Which made it a HELL of a lot simpler than having to track where in the array I was at, as I first figured I thought I'd have to.. :)
Anyway, I'll accept your answer, but here's the code I ended up doing: (The actual replacing of the word in the textbox I've not implemented yet.)
private void btnSpelling_Click(object sender, RoutedEventArgs e)
{
SpellChecklistBox.Items.Clear();
string[] articleWordsArray = txtArticle.Text.Split(' ');
foreach (string word in articleWordsArray)
{
articleWords.Enqueue(word);
}
CorrectWord();
}
private void SpellChecklistBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
SpellCheckerPopup.IsOpen = false;
}
private void SpellCheckerPopup_Closed(object sender, EventArgs e)
{
CorrectWord();
SpellChecklistBox.Items.Clear();
}
Queue<string> articleWords = new Queue<string>();
private void CorrectWord()
{
if (articleWords.Count() > 0)
{
string checkedWord = articleWords.Dequeue();
bool success = _spellChecker.CheckWord(checkedWord);
List<string> suggest;
if (!success)
{
suggest = _spellChecker.GetSuggestions(checkedWord);
foreach (string s in suggest)
{
SpellChecklistBox.Items.Add(new ListBoxItem() { Content = s });
}
SpellCheckerPopup.IsOpen = true;
SpellChecklistBox.Items.Add(new ListBoxItem() { Content = " ----------------------" });
SpellChecklistBox.Items.Add(new ListBoxItem() { Content = "Ignore" });
SpellCheckerPopup.IsOpen = true;
}
}
}
It's all pretty straight forward courtesy of the Queue datatype. When the spelling button gets clicked it loads the TextBox into an array, and then I loop through the array to enqueue the items into the articleWords queue, after which it calls CorrectWord(). CorrectWord() then loads the relevant list after dequeueing from articleWords, and on the PopUp. Closed event it clears the ListBox and calls CorrectWord() which will keep bringing back the PopUp until there are no more words to be corrected. :)

Categories

Resources