问题
I have a win GUI application written in C#, in which I have a TextBox
component that I write my log to. at some point it gets too loaded and the entire app is starting to falter.
I'd like to make an efficient mechanism to make it a FIFO, meaning - make it a fixed size and deleting the oldest content automatically.
is there any .net/c# feature for doing that? otherwise, what will be the right way to do this?
UPDATE: I also have this issue with other sort of textual data, and not only with logs. therefor, the ListBox
is not a proper solution for me.
回答1:
To create a circular buffer for text, I'd use a StringBuilder
, with the capacity set to about twice the amount of data I want to display.
const int DisplaySize = 10000;
StringBuilder fifo = new StringBuilder(2 * DisplaySize);
string AppendToFifo( string s )
{
if (s.Length >= DisplaySize) {
// FACT: the display will only include data from s
// therefore, toss the entire buffer, and only keep the tail of s
fifo.Clear();
fifo.Append(s, s.Length - DisplaySize, DisplaySize);
return fifo.ToString();
}
if (fifo.Length + s.Length > fifo.Capacity) {
// FACT: we will overflow the fifo
// therefore, keep only data in the fifo that remains on the display
fifo.Remove(0, fifo.Length + s.Length - DisplaySize);
}
fifo.Append(s);
if (fifo.Length <= DisplaySize) {
// FACT: the entire fifo content fits on the display
// therefore, send it all
return fifo.ToString();
}
// FACT: the fifo content exceed the display size
// therefore, extract just the tail
return fifo.ToString(fifo.Length - DisplaySize, DisplaySize);
}
The fast path, when none of the if conditions are true, avoids all unnecessary copies (in the .NET world where strings are immutable, the final copy to create the output string cannot be avoided). And in the other cases only needed characters are copied. Increasing the capacity of the buffer will improve utilization fraction of the fast path. What I've been careful to avoid doing is creating a string object with the old content that remains on the display, which has no purpose except to concatenate with the new content, and immediately becomes garbage.
Obviously, if you use p/invoke to pass a pointer to the StringBuffer's content instead of copying out a substring, that will be even more efficient.
回答2:
While I'm sure there is a better solution - when I think FIFO - I think Queue. So, you could do something like make a Queue of strings to add your log items to and set up an integer to represent max number of log items.
private Queue<string> logQueue = new Queue<string>();
private const int logMax = 100;
then you could set up a logging method like so.
public void Log(string logText)
{
// this should only ever run for 1 loop as you should never go over logMax
// but if you accidentally manually added to the logQueue - then this would
// re-adjust you back down to the desired number of log items.
while (logQueue.Count > logMax - 1)
logQueue.Dequeue();
logQueue.Enqueue(logText);
textBox.Text = string.Join(Environment.NewLine, logQueue.ToArray());
}
This should get you the functionality that you are looking for.
The one obvious drawback with this approach - is that you have your text stored in memory twice. Once in the queue and once as a concatenated string in the text box. If this isn't a big issue for you - then this may work.
回答3:
You could try something like this.
public void updateMyTextBox( string newText )
{
// Get the current text, and append the newText to the end
string text = MyTextBox.Text;
text += newText;
// Ensure text does not exceed maximum length
if( text.Length > MaxLength ) // Max Length constant declared elsewhere
text = text.Substring( text.Length - MaxLength );
// Update MyTextBox
myTextBox.Text = text;
}
This is a very simple solution, so you'd need to do some extra work to get it to check for new lines and other such conditions, but it's where I would start.
回答4:
You can use the ListBox
control and add new items to the beginning of the list, like this:
ListBox1.Items.Insert(0, "Newest Item");
Then you can remove the oldest from the list box, like this:
ListBox1.Items.RemoveAt(ListBox1.Items.Count - 1);
回答5:
It kind of depends on your needs and the look you are after.
The approach I took was to creat A Scrolling Status Control that automatically tracks the last n lines of output, scrolls as needed, and renders the output correctly.
I was kind of going for the scrolling text window at the bottom of Visual Studio such as during compilation, but I ended up with something far less functional. But it is extremely light on resources. You might take a look and see if something similar would work for you.
回答6:
// LIFO Stack simple
private void btnSend_Click(object sender, EventArgs e) // on button press
{
string oldText = rtbReceive.Text; // copy old text from richTextBox
rtbReceive.Clear(); // Clear richTextBox
rtbReceive.AppendText(tbSend.Text + "\r\n"); // add new text from textBox to richTextBox
rtbReceive.AppendText(oldText); // add old text to richTextBox
// Best Regards by Petar Ivanov Upinov (BG)
}
来源:https://stackoverflow.com/questions/18988170/making-a-net-textbox-work-fifo-style