Quickest way to Update Multiline Textbox with Large Amount of Text - c#

I have a .NET 4.5 WinForm program that queries a text-based database using ODBC. I then want to display every result in a multiline textbox and I want to do it in the quickest way possible.
The GUI does not have to be usable during the time the textbox is being updated/populated. However, it'd be nice if I could update a progress bar to let the user know that something is happening - I believe a background worker or new thread/task is necessary for this but I've never implemented one.
I initially went with this code and it was slow, as it drew out the result every line before continuing to the next one.
OdbcDataReader dbReader = com.ExecuteReader();
while (dbReader.Read())
{
txtDatabaseResults.AppendText(dbReader[0].ToString());
}
This was significantly faster.
string resultString = "";
while (dbReader.Read())
{
resultString += dbReader[0].ToString();
}
txtDatabaseResults.Text = resultString;
But there is a generous wait time before the textbox comes to life so I want to know if the operation can be even faster. Right now I'm fetching about 7,000 lines from the file and I don't think it's necessary to switch to AvalonEdit (correct me if my way of thinking is wrong, but I would like to keep it simple and use the built-in textbox).

You can make this far faster by using a StringBuilder instead of using string concatenation.
var results = new StringBuilder();
while (dbReader.Read())
{
results.Append(dbReader[0].ToString());
}
txtDatabaseResults.Text = results.ToString();
Using string and concatenation creates a lot of pressure on the GC, especially if you're appending 7000 lines of text. Each time you use string +=, the CLR creates a new string instance, which means the older one (which is progressively larger and larger) needs to be garbage collected. StringBuilder avoids that issue.
Note that there will still be a delay when you assign the text to the TextBox, as it needs to refresh and display that text. The TextBox control isn't optimized for that amount of text, so that may be a bottleneck.
As for pushing this into a background thread - since you're using .NET 4.5, you could use the new async support to handle this. This would work via marking the method containing this code as async, and using code such as:
string resultString = await Task.Run(()=>
{
var results = new StringBuilder();
while (dbReader.Read())
{
results.Append(dbReader[0].ToString());
}
return results.ToString();
});
txtDatabaseResults.Text = resultString;

Use a StringBuilder:
StringBuilder e = new StringBuilder();
while (dbReader.Read())
{
e.Append(dbReader[0].ToString());
}
txtDatabaseResults.Text = e.ToString();

Despite the fact that a parallel Thread is recommended, the way you extract the lines from file is somehow flawed. While string is immutable everytime you concatenate resulString you actually create another (bigger) string. Here, StringBuilder comes in very useful:
StringBuilder resultString = new StringBuilder ()
while (dbReader.Read())
{
resultString = resultString.Append(dbReader[0].ToString());
}
txtDatabaseResults.Text = resultString;

I am filling a regular TextBox (multiline=true) in a single call with a very long string (more than 200kB, loaded from a file. I just assign the Text property of TextBox with my string).
It's very slow (> 1 second).
The Textbox does anything else than display the huge string.
I used a very simple trick to improve performances : I replaced the multiline textbox by a RichTextBox (native control).
Now same loadings are instantaneous and RichTextBox has exactly the same appearance and behavior as TextBox with raw text (as long as you didn't tweaked it). The most obvious difference is RTB does not have Context menu by default.
Of course, it's not a solution in every case, and it's not aiming the OP question but for me it works perfectly, so I hope it could help other peoples facing same problems with Textbox and performance with big strings.

Related

WPF TextBox binding to StringBuilder to simulate Console Performance

So I want redirect my Console output to a TextBox to simulate in-GUI console. Now I saw this post:
https://stackoverflow.com/a/18727100/6871623
Which helps the redirection process.
However, since string is immutable, this means a new string will be allocated on every write and this isn't very efficient.
Therefore, I thought of using StringBuilder like:
public class ControlWriter : TextWriter
{
private Control textbox;
private StringBuilder builder;
public ControlWriter(Control textbox)
{
this.textbox = textbox;
builder = new StringBuilder();
}
public override void Write(char value)
{
builder.Append(value);
textbox.Text = builder.ToString();
}
public override void Write(string value)
{
builder.Append(value);
textbox.Text = builder.ToString();
}
public override Encoding Encoding
{
get { return Encoding.ASCII; }
}
}
Looking at the code, it doesn't seem to improve performance much since a new string will be allocated every time we call builder.ToString(), mainly what we improve by this is the Append portion since now we won't be using string concat every time.
Is there a way to bind TextBox Text directly to StringBuilder? That is, appending or resetting the StringBuilder will automatically be reflected on GUI?
If that isn't possible for TextBox, is there another way of going around this?
Finally, is there a way to improve performance of the code above?
Since this is all hypothetical, I'll answer with what I can.
The Console isn't a string, or a StringBuilder, it's actually a buffer, a multi-dimensional array of char (term used loosely).
A TextBox is backed by a string.
A StringBuilder is a single dimensional array of char, that can convert to a string with ToString()
So, updating a string with and endless stream of chars is going to bottom out sooner or later. Meaning, at some point you are going to be splattering a TextBox with endless amounts of characters that have no concept of lines.
If you want to get this more realistic, maybe you want a List or array instead of a string, but that makes its own problems. However, doing so will allow you to at least buffer a certain amount of data, or at least virtualise it in some way.
Also, you can't get over the allocations here, every time you do something with a string, you are allocating again.
Then you talk about binding... Binding is not going to help the allocations.
To answer your questions, you need to ask your self more questions. What is the best way you can visualise the data, can you virtualise it, how are you going to deal with lines.
For a simple exercise the textbox and what you are doing is fine. However, suggesting anything else on top of this is exceedingly hard and too broad.

Highlighting in a RichTextBox is taking too long

I have a large list of offsets which I need to highlight in my RichTextBox. However this process is taking too long. I am using the following code:
foreach (int offset in offsets)
{
richTextBox.Select(offset, searchString.Length);
richTextBox.SelectionBackColor = Color.Yellow;
}
Is there a more efficient way to do so?
UPDATE:
Tried using this method but it doesn't highlight anything:
richTextBox.SelectionBackColor = Color.Yellow;
foreach (int offset in offsets)
{
richTextBox.Select(offset, searchString.Length);
}
I've googled your issue and I found that RichTextBox is getting very slow when having many lines. In my opinion, you have either buy a third part control which you can be satisfied by its performance or you may need threads to devide the whole selection task. I think they can accelerate things up.
Hope it helps !
I've had this same problem before. I ended up disregarding all of the methods they give you and manipulated the underlying RTF data. Also, the reason that your second block of code doesnt work is that RTF applies formatting as it goes, so if you call a function (or Property in this case) to change the selection color, it will only apply it for the currently selected block. Any changes made to the selection after that call become irrelavent.
You can play around with the RGB values, or here is a great source on how to do different things within the RTF control. Pop this function in your code and see how well it works. I use it to provide realtime syntax highlighting for SQL code.
public void HighlightText(int offset, int length)
{
String sText = richTextBox.Text.Trim();
sText = sText.Insert(offset + length - 1, #" \highlight0");
sText = sText.Insert(offset, #" \highlight1");
String s = #"{\rtf1\ansi\deff0{\fonttbl{\f0\fnil\fcharset0 Courier New;}}
{\colortbl ;\red255\green255\blue0;}\viewkind4\uc1\pard";
s += sText;
s += #"\par}";
richTextBox.Rtf = s;
}
Does it make any difference if you set the SelectionBackColor outside of the loop?
Looking into the RichTextBox with Reflector shows, that a WindowMessage is sent to the control every time when the color is set. In the case of large number of offsets this might lead to highlighting the already highlighted words again and again, leading to O(n^2) behavior.

How could I read a very large text file using StreamReader?

I want to read a huge .txt file and I'm getting a memory overflow because of its sheer size.
Any help?
private void button1_Click(object sender, EventArgs e)
{
using (var Reader = new StreamReader(#"C:\Test.txt"))
{
textBox1.Text += Reader.ReadLine();
}
}
Text file is just:
Line1
Line2
Line3
Literally like that.
I want to load the text file to a multiline textbox just as it is, 100% copy.
Firstly, the code you posted will only put the first line of the file into the TextBox. What you want is this:
using (var reader = new StreamReader(#"C:\Test.txt"))
{
while (!reader.EndOfStream)
textBox1.Text += reader.ReadLine();
}
Now as for the OutOfMemoryException: I haven't tested this, but have you tried the TextBox.AppendText method instead of using +=? The latter will certainly be allocating a ton of strings, most of which are going to be nearly the length of the entire file by the time you near the end of the file.
For all I know, AppendText does this as well; but its existence leads me to suspect it's put there to deal with this scenario. I could be wrong -- like I said, haven't tested personally.
You'll get much faster performance with the following:
textBox1.Text = File.ReadAllText(#"C:\Test.txt");
It might also help with your memory problem, since you're wasting an enormous amount of memory by allocating successively larger strings with each line read.
Granted, the GC should be collecting the older strings before you see an OutOfMemoryException, but I'd give the above a shot anyway.
First use a rich text box instead of a regular text box. They're much better equiped for the large amounts of data you're using. However you still need to read the data in.
// use a string builer, the += on that many strings increasing in size
// is causing massive memory hoggage and very well could be part of your problem
StringBuilder sb = new StringBuilder();
// open a stream reader
using (var reader = new StreamReader(#"C:\Test.txt"))
{
// read through the stream loading up the string builder
while (!reader.EndOfStream)
{
sb.Append( reader.ReadLine() );
}
}
// set the text and null the string builder for GC
textBox1.Text = sb.ToString();
sb = null;
Read and process it one line at a time, or break it into chunks and deal with the chunks individually. You can also show us the code you have, and tell us what you are trying to accomplish with it.
Here is an example: C# Read Text File Containing Data Delimited By Tabs Notice the ReadLine() and WriteLine() statements.
TextBox is severely limited by the number of characters it can hold. You can try using the AppendText() method on a RichTextBox instead.

How can I add a huge string to a textbox efficiently?

I have a massive string (we are talking 1696108 characters in length) which I have read very quickly from a text file. When I add it to my textbox (C#), it takes ages to do. A program like Notepad++ (unmanaged code, I know) can do it almost instantly although Notepad takes a long time also. How can I efficiently add this huge string and how does something like Notepad++ do it so quickly?
If this is Windows Forms I would suggest trying RichTextBox as a drop-in replacement for your TextBox. In the past I've found it to be much more efficient at handling large text. Also when making modifications in-place be sure to use the time-tested SelectionStart/SelectedText method instead of manipulating the Text property.
rtb.SelectionStart = rtb.TextLength;
rtb.SelectedText = "inserted text"; // faster
rtb.Text += "inserted text"; // slower
Notepad and Window TextBox class is optimized for 64K text. You should use RichTextBox
You could, initially, just render the first n characters that are viewable in the UI (assuming you have a scrolling textbox). Then, start a separate thread to render successive blocks asynchronously.
Alternatively, you could combine it with your input stream from the file. Read a chunk and immediately append it to the text box. Example (not thorough, but you get the idea) ...
private void PopulateTextBoxWithFileContents(string path, TextBox textBox)
{
using (var fs = File.OpenRead(path))
{
using (var sr = new StreamReader(fs))
{
while (!sr.EndOfStream)
textBox.Text += sr.ReadLine();
sr.Close();
}
fs.Close();
}
}

Clearing a TextBox in ASP.NET

I have webpage where there is textbox with some default value. I have to clear that value from the textbox. I have two options:
textbox.text="";
or
textbox.text.remove(0,length);
Which one should I use? Does it make any impact on page performance (there are many textboxes placed on my page)?
The best way to do this is
textbox.text = string.Empty;
Also remember that string type is immutable!
It makes no difference - do what is most readable for you and your colleagues.
Many prefer to use string.Empty.
The performance difference between the two options will be too small to measure, most likely.
TextBox.Text = String.Empty; is a lot more readable. It clearly states what you're trying to do: "set the text property of this text box to an empty string".
I recommend you go with the assignment, as it is both faster, and much more clear.
Presumably, you mean clear the value with javascript when a user clicks int the box? If so, it won't make any difference to performance.
I use jQuery in most pages, so I just hook up this function to clear default values onClick, using the ClientId of the textbox:
$('#ctl00_TextBox').click(function() { $('#ctl00_TextBox').val('');
If you mean clear it in codebehind use this:
yourTextBox.Text = String.Empty;
textbox.text="";
or
foreach (var item in Page.Controls)
{
if (item is TextBox)
{
((TextBox)item).Text = "";
}
}
The impact on performance will be minimal even with hundreds/thousands of calls, however
textbox.text = "";
should in theory be very slightly quicker, since you're just assigning a new value rather than processing the string (as .Remove does)
The best way to do this would be to do
textbox.text = String.Empty;
Update: if you're counting clock-cycles, String.Empty will actually execute faster as well, because it doesn't create a new object, whereas "" will create a new String object.
However, you should really not be too concerned about this, there aren't many ways to set a string to empty that will cause performance issues! You should use whichever is the most readable...

Categories