Reputation: 1888
Currently my application have a textbox that will show 500 lines of live logging on the screen. Since the application have alot of heavy task running behind, so i try to improve this part for my application as much as i can. Open for any suggestion. Thanks.
WPF:
<TextBox Text="{Binding StatusMessage}" TextWrapping="Wrap" AcceptsReturn="True" />
Method 1:
private void OutputMessage(string msg)
{
//Result
//1000 Lines 108ms
//10000 Lines 1687ms
if(!string.IsNullOrEmpty(msg))
{
string msgWithTimeStamp = System.DateTime.Now.ToString() + ": \t" + msg;
int msgLine = 0;
if (!string.IsNullOrEmpty(StatusMessage))
{
//Only show 500 lines of latest messages...
msgLine = StatusMessage.Count(m => m == '\n');
if (msgLine > 500)
StatusMessage = StatusMessage.Remove(0, StatusMessage.IndexOf("\n") + 1);
StatusMessage = StatusMessage + "\n" + msgWithTimeStamp;
}
else
StatusMessage = msgWithTimeStamp;
}
}
Method 2 (Better performance):
List<string> msgList;
private void OutputMessage(string msg)
{
//Result
//1000 Lines 31ms
//10000 Lines 377ms
if (!string.IsNullOrEmpty(msg))
{
string msgWithTimeStamp = System.DateTime.Now.ToString() + ": \t" + msg;
msgList.Add(msgWithTimeStamp);
if (msgList.Count > 500)
msgList.RemoveAt(0);
StatusMessage = String.Join("\n", msgList);
}
}
Upvotes: 1
Views: 559
Reputation: 22946
Instead of using a TextBox you could use an ItemsControl and data bind its ItemsSource to an ObservableCollection. Then you just append each new message to the ObservableCollection and the ItemsControl will automatically update to show the new entry at the end of its list.
Upvotes: 4
Reputation: 4672
The biggest problem is the following line:
StatusMessage = String.Join("\n", msgList);
This means your rebuilding the entire string every time the main list of strings is updated, and it loops through the entire list, allocating new memory buffers until the buffer is large enough to fill the entire string (all 500 lines). When your message list grows to 500 lines, it means the next message will cause 500 buffer allocations just to rebuild the full string.
A better way would be to maintain a StringBuilder
alongside your list of messages (in your msgList
variable). StringBuilder
is smart in how it allocates buffers as you modify your messages.
I wrote the following snippet, and on my machine the time it takes to add a new message to the list that already has 500 messages in it is less than a millisecond.
// Somewhere in your code, probably next to your [msgList] variable...
private StringBuilder sb = new StringBuilder();
private void OutputMessage(string msg)
{
if (!string.IsNullOrEmpty(msg))
{
string msgWithTimeStamp = System.DateTime.Now.ToString() + ": \t" + msg;
msgList.Add(msgWithTimeStamp);
sb.AppendLine(msgWithTimeStamp);
if (msgList.Count > 500)
{
sb.Remove(0, msgList[0].Length + 2); // The +2 is to account for the new line and carriage return characters.
msgList.RemoveAt(0);
}
statusMessage = sb.ToString();
}
}
The only downside to this approach is the maintenance of the same set of messages twice.
Upvotes: 3