How Can I Prevent RichTextBox Append which Can Cause OutOfMemory? - c#

My Objective is keep logs line by line with RichtextBox control, But I am worry that when the lines reach to certain point , my window form will be hung or run of out memory..
Can any one show me how can i prevent this will happen, I have in mind maybe limit 300 lines with FIFO , or 500 lines then empty and refresh again..However i am not sure How Can i implement this.
void WriteLog(string txt)
{
richTextBox1.AppendText(txt + Environment.NewLine);
richTextBox1.HideSelection = false;
richTextBox1.SelectionStart = richTextBox1.Text.Length;
}

If you want to delete lines than try to use this
void WriteLog(string txt)
{
if(richTextBox1.Lines.Count() == 100)
{
DeleteLine(0);
}
richTextBox1.AppendText(txt + Environment.NewLine);
richTextBox1.HideSelection = false;
richTextBox1.SelectionStart = richTextBox1.Text.Length;
}
private void DeleteLine(int a_line)
{
int start_index = richTextBox1.GetFirstCharIndexFromLine(a_line);
int count = richTextBox1.Lines[a_line].Length;
// Eat new line chars
if (a_line < richTextBox1.Lines.Length - 1)
{
count += richTextBox1.GetFirstCharIndexFromLine(a_line + 1) -
((start_index + count - 1) + 1);
}
richTextBox1.Text = richTextBox1.Text.Remove(start_index, count);
}

try this code to remove last line and then append text then you'll have just 300lines limit:
private void RemoveLastLineAfter300()
{
if(richTextBox1.TextLength != 0)
{
int totalCharacters = richTextBox1.Text.Trim().Length;
int totalLines = richTextBox1.Lines.Length;
string lastLine = richTextBox1.Lines[totalLines - 1] + "\n";
string copyOfLastLine = richTextBox1.Lines[totalLines - 1];
if(totalLines > 300)
{
string newstring = richTextBox1.Text.Substring(0, totalCharacters - lastLine.Length);
richTextBox1.Text = newstring;
}
}
}
And if you want to clear text(if I undertood correctly) after 500lines just check on TextChanged event
if(richTextBox1.Lines.Length > 500)
richTextBox1.Text = string.Empty;
I hope this helps you.

Related

Enter button does not work at textbox

I'm a beginner in c# and first time need your help, because I can't find any solution to this problem.
I make a typewriting tutorial, I have a richtextbox filled with randomly generated characters, spaces and 3 new line.
The user must type the characters in a textbox, and if he typed the adequate character the character turn to green in richtextbox and he can type the the next one. If he makes a mistake, he cant move to next character until the the right button was pressed.
My problem is everything works, except new line part.
When the program compare the 2 new line accept the new line but just when ctrl+enter was pressed, simple enter wont work, the program somehow thinks enter is a wrong character.
I need to make this user friendly and a simple enter needed for this.
I tried this so far:
Make textbox MultiLine makes no difference.
Changed Acceptsreturn to true makes no difference.
Changed in string that will be the text of richtextbox the Environment.Newline to \r and \n and \r\n and its makes no difference.
Changed the textbox to Richtextbox makes no difference.
I tried every possible combination above and I cant work out what is the problem.
Here it is the random character generating part:
public partial class Form1 : Form
{
char[] karakterek = { 'a','á','b','c','d','e','é','f','g','h','i','í','j','k','l','m','n','o','ó','ö','ő','p','q','r','s','t','u','ú','ü','ű','v','x','y','z' };
char[] nemkarakterek = {'0','1','2','3','4','5','6','7','8','9','"',',','+','%','/','=',':','-',};
char egykarakter;
string teljes = "";
int mutato = 0;
Random r = new Random();
public Form1()
{
InitializeComponent();
karakterfeltolt();
}
private void karakterfeltolt()
{
int hanyszor = 0;
do
{
string egesz = "";
if (hanyszor < 3)
{
do
{
if (egesz.Length < 150)
{
if (r.Next(1,15)<14)
{
egykarakter = karakterek[r.Next(0, karakterek.Length)];
egesz = egesz + egykarakter;
}
else
{
egykarakter = nemkarakterek[r.Next(0, nemkarakterek.Length)];
egesz = egesz + egykarakter;
}
}
} while (egesz.Length < 150);
if (egesz.Length > 150)
{
egesz = egesz.Substring(0, 150);
}
int vanespace = 0;
egesz = egesz.Insert(r.Next(2, 10), " ");
for (int i = 0; i < egesz.Length - 10; i++)
{
if (egesz[i] == ' ')
{
vanespace = i;
int eselynovelo = r.Next(0, 10);
if (eselynovelo > 6)
{
egesz = egesz.Insert(r.Next(vanespace + 3, vanespace + 10), " ");
}
else
{
egesz = egesz.Insert(r.Next(vanespace + 6, vanespace + 10), " ");
}
}
}
string nagybetus = egesz.Substring(0, 1).ToUpper() + egesz.Substring(1);
nagybetus = nagybetus.Insert(nagybetus.Length, ".\r\n");
teljes = teljes + nagybetus;
hanyszor++;
}
} while (hanyszor < 3);
richTextBox1.Text = teljes;
}
And here is the textbox compare part:
private void textBox3_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == richTextBox1.Text[mutato])
{
richTextBox1.SelectionStart = 0;
richTextBox1.SelectionLength = mutato + 1;
richTextBox1.SelectionColor = Color.Green;
mutato++;
label2.Text = "OK";
}
else
{
label2.Text = "Wrong";
e.Handled = true;
}
}
What else can I do? Please help me.

Hyperlink to a line of RichTextBox in C#

I am looking a way to create a HyperLink in a RichTextBox pointing to a line of the text of the same RichTextBox.
I just found how to do that with Internet Links but I don't find a way to do it with the same text inside of the control (It's like Hyperlinks in MS Word pointing to a header or bookmark).
Thanks in Advance. - CCB
No, this will not work unless you code the necessary stuff yourself.
Two suggestions:
A simple workaround with links always starting with www.
The nicer solution with arbitrary link text
Let's have a look at both options..:
Using the built-in functionality of recognizing an URL seems the right way to start, but the link will always have to look like a URL, not like a hyperlink to an anchor.. If you can live with a solution that has, say, links like this: www.goto.234 and anchors like this: #234# this is really rather simple..
A working example can be as simple as this:
private void richTextBox1_LinkClicked(object sender, LinkClickedEventArgs e)
{
var s = e.LinkText.Split('.');
string anchor = s[2];
int a = richTextBox1.Text.IndexOf("#" + anchor + "#" );
if (a >= 0) richTextBox1.SelectionStart = a; else return; // do add more checks!
richTextBox1.SelectionLength = 0;
Text = anchor + " # " + a;
//richTextBox1.ScrollToCaret(); <<--- this crashes on my machine!
// so I take the jump out of the click event and it works:
Timer ttt = new Timer() { Interval = 100 };
ttt.Tick += (ss, ee) => { richTextBox1.ScrollToCaret(); ttt.Stop(); };
}
Option two: If you'd rather have more choice of how the links should read you can do this:
Start by formatting each to
Start with a special character, say a tilde '~'
format it to look blue and underlined if you want to
Either make it one word or replace space by underlines and format those to have the forecolor equal to the backcolor
Now this can do the job:
public string delimiters = " ()[]{}!&?=/\\,;.\r\n";
private void richTextBox2_Click(object sender, EventArgs e)
{
int sstart = -1;
string s = getWordAt(richTextBox2.Text,
richTextBox2.SelectionStart, delimiters, out sstart);
if (s.Length < 3) return;
string char1 = s.Substring(0, 1);
if (char1 == "~")
{
int p = richTextBox2.Text.IndexOf("#" + s.Substring(1));
if (p >= 0) { richTextBox2.SelectionStart = p; richTextBox2.ScrollToCaret(); }
}
}
public static string getWordAt(string text, int cursorPos,
string delimiters, out int selStart)
{
int startPos = 0;
selStart = startPos;
if ((cursorPos < 0) | (cursorPos > text.Length) | (text.Length == 0)) return "";
if ((text.Length > cursorPos) & (delimiters.Contains(text[cursorPos]))) return "";
int endPos = text.Length - 1;
if (cursorPos == text.Length) endPos = text.Length - 1;
else { for (int i = cursorPos; i < text.Length; i++)
{ if (delimiters.Contains(text[i])) { endPos = i - 1; break; } } }
if (cursorPos == 0) startPos = 0;
else { for (int i = cursorPos; i > 0; i--)
{ if (delimiters.Contains(text[i])) { startPos = i + 1; break; } } }
selStart = startPos;
return text.Substring(startPos, endPos - startPos + 1);
}
Here are the two versions side by side, once at the top then after clicking on a link:
Both versions work fine, although both could do with some more checks.
Note that I was too lazy to format the pseudo-links in the second example, so they show their tildes and hashes..
Not hard to write a helper function that can insert the formatting; the search will still work as it searches in the Text, not the Rtf property..

Detecting repetition of part of received data

Based on the code shown.. Am I writing the right coding if i want to compare the data that were being stream in? Basically starting from the part
while(serialPort1.IsOpen)
For instance first string of data received was T 12 29.5 then next string was T 12 29.5 followed by T 20 24.5 and on so.. basically unpredictable what going to be received next.
I want to program to be able to detect/count the number of appearance for the middle value..like...
====================
[number] | [Repeated times]
12 | 2
=================== but when another different number received,
[number] | [Repeated]
20 | 1
=================== the counter for the number will be overwrite and reset whenever a different number was received.
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
string time = DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss.ff");
RxString = serialPort1.ReadLine();
string[] split = RxString.Split('.');
string dp = split[1];
Char c = dp[0];
split[1] = c.ToString();
RxString = split[0] + "." + split[1];
while (serialPort1.IsOpen)
{
string[] number = RxString.Split(' ');
string unit = number[1];
int count = 1;
for(int i = 1; i < unit.Count(); i++)
{
if(unit[i-1] == unit[i])
count++;
else
count = 1;
if(count == 4)
{
//execute some parameters
}
}
}
this.Invoke(new EventHandler(DisplayText));
StreamWriter MyStreamWriter = new StreamWriter(#"C:\Users\acer\Documents\Data3.txt", true);
MyStreamWriter.Write(time + " " + RxString + "\r\n");
MyStreamWriter.Flush();
MyStreamWriter.Close();
}
EDIT V2
Why wont the prog record data which only has count of 1?
string[] number = RxString.Split(' '); //split RxString by ' '
string unit = number[1]; //unit = unit no.
int count = 1;
for (int i = 1; i < unit.Count(); i++)
{
if (unit[i - 1] == unit[i])
count++;
else
{
count = 1;
StreamWriter MyStreamWriter = new StreamWriter(#"C:\Users\acer\Documents\Data3.txt", true); //True tell SW to append to file instead of overwriting
MyStreamWriter.Write(time + " " + RxString + "\r\n"); //Write time + string
MyStreamWriter.Flush();
MyStreamWriter.Close();
}
You should use a dictionary to store each element and its own count :
var dict = new Dictionary<string, int?>();
while (serialPort1.IsOpen)
{
string[] number = RxString.Split(' ');
string unit = number[1];
if (dict.ContainsKey(unit))
{
if (dict[unit].HasValue)
{
dict[unit]++;
if (dict[unit] == 4)
{
// execute some parameters
dict[unit] = null;
}
}
}
else
{
dict.Add(unit, 1);
}
}
I'd create a special struct for that:
struct DataInfo
{
public string Number { get; set; }
public int Counter { get; set; }
... Other data you require to work with
}
And use either List<DataInfo> or Dictionary<string, DataInfo> to store values;

c# tab wrapped string

I am writing out a large string (around 100 lines) to a text file, and would like the entire block of text tabbed.
WriteToOutput("\t" + strErrorOutput);
The line I am using above only tabs the first line of the text. How can I indent/tab the entire string?
Replace all linebreaks by linebreak followed by a tab:
WriteToOutput("\t" + strErrorOutput.Replace("\n", "\n\t"));
File.WriteAllLines(FILEPATH,input.Split(new string[] {"\n","\r"}, StringSplitOptions.None)
.Select(x=>"\t"+x));
To do so you would have to have a limited line length (ie <100 characters) at which point this issue becomes easy.
public string ConvertToBlock(string text, int lineLength)
{
string output = "\t";
int currentLineLength = 0;
for (int index = 0; index < text.Length; index++)
{
if (currentLineLength < lineLength)
{
output += text[index];
currentLineLength++;
}
else
{
if (index != text.Length - 1)
{
if (text[index + 1] != ' ')
{
int reverse = 0;
while (text[index - reverse] != ' ')
{
output.Remove(index - reverse - 1, 1);
reverse++;
}
index -= reverse;
output += "\n\t";
currentLineLength = 0;
}
}
}
}
return output;
}
This will convert any text into a block of text that is broken up into lines of length lineLength and that all start with a tab and end with a newline.
You could make a copy of your string for output that replaces CRLF with CRLF + TAB. And the write that string to output (still prefixed with the initial TAB).
strErrorOutput = strErrorOutput.Replace("\r\n", "\r\n\t");
WriteToOutput("\t" + strErrorOutput);
If you're here looking for a way to word-wrap a string to a certain width, and have each line indented (as I was), here's a solution as an extension method. Roughly based on the answer above, but uses regular expressions to split the original text into word and spaces pairs, then rejoins them, adding line breaks and indentation as needed. (Does not sanity check inputs, so you'll need to add that if needed)
public string ToBlock(this string text, int lineLength, string indent="")
{
var r = new Regex("([^ ]+)?([ ]+)?");
var matches = r.Match(text);
if (!matches.Success)
{
return text;
}
string output = indent;
int currentLineLength = indent.Length;
while (matches.Success)
{
var groups = matches.Groups;
var nextLength = groups[0].Length;
if (currentLineLength + nextLength <= lineLength)
{
output += groups[0];
currentLineLength += groups[0].Length;
}
else
{
if (currentLineLength + groups[1].Length > lineLength)
{
output += "\n" + indent + groups[0];
currentLineLength = indent.Length + groups[0].Length;
}
else
{
output += groups[1] + "\n" + indent;
currentLineLength = indent.Length;
}
}
matches = matches.NextMatch();
}
return output;
}

Looking for a tag in a txt doc

Ok I tried all the things that you all sugested, but it is still not working for me. I am not sure why but it will not kick out the output sometimes and sometimes it it puts an out put that is not right. What am I doing wrong.
InPutBox = Input.Text;
int x = 1;
var lines = Input.Text.Split(new string[] { Environment.NewLine },StringSplitOptions.None);
for (var index = 0; index < lines.Length; index++)
{
var line = lines[index];
do
{
x++;
System.Console.WriteLine("'{0}'", InPutBox);
bool test1 = InPutBox.StartsWith("11600");
bool test2 = InPutBox.EndsWith("(");
if (test1 && test2)
{
int first = InPutBox.IndexOf("11600");
int last = InPutBox.LastIndexOf("(");
InPutBox = InPutBox.Substring(first, last - first);
}
}
while (x < 50);
System.Console.WriteLine("'{0}'", line);
if ((line.StartsWith("11600") && line.EndsWith("(")))
{
MessageBox.Show("These are errors in line" + index + ": " + line);
break;
}
}
This:
InPutBox = InPutBox.Substring(last, first - last);
probably needs to be this:
InPutBox = InPutBox.Substring(first, last - first);
May need to add or subtract 1 depending on whether or not you want to include the W or 0. Run it in the debugger to see the actual numbers and you should be able to diagnose more quickly.
Check your conditions. With your current code, .Substring will throw an error if last is not found (because startIndex cannot be less than zero).
You probably want this:
bool test1 = InPutBox.StartsWith("W");
bool test2 = InPutBox.EndsWith("0");
if (test1 && test2) {
int first = InPutBox.IndexOf("W");
int last = InPutBox.LastIndexOf("0");
InPutBox = InPutBox.Substring(last, first - last);
}
#D Stanley is probably right in that the last line is meant to be reversed as well
InPutBox = InPutBox.Substring(first, last - first);
private void InPutBoxMethod()
{
// split text into lines
var lines = textBox1.Text.Split(new string[] {Environment.NewLine}, StringSplitOptions.None);
// loop through all lines and check for errors
for (var index = 0; index < lines.Length; index++)
{
var line = lines[index];
System.Console.WriteLine("'{0}'", line);
if ((line.StartsWith("W") && line.EndsWith("0")))
{
MessageBox.Show("These are errors in line " + index + ": " + line);
break;
}
}
}

Categories