我有一个用于显示日志消息的 LogTextBox 类:
public class LogTextBox : TextBox
{
int maxMessageCount, messageCount;
//number of characters for each message
List<int> messageLengths;
public LogTextBox(int maxMessageCount)
{
this.messageCount = 0;
this.maxMessageCount = maxMessageCount;
this.messageLengths = new List<int>();
IsReadOnly = true;
IsUndoEnabled = false;
}
public void Log(string message)
{
if (messageCount >= maxMessageCount)
{
Dispatcher.Invoke((Action)delegate()
{
//statement 1
string text = Text.Remove(0, messageLengths[0]);
//statement 2
Text = text + message + '\n';
//statement 3
ScrollToEnd();
});
messageLengths.RemoveAt(0);
messageLengths.Add(message.Length + 1);
}
else
{
Dispatcher.Invoke((Action)delegate()
{
AppendText(message + '\n');
ScrollToEnd();
});
messageLengths.Add(message.Length + 1);
messageCount++;
}
}
}
public class Test
{
public LogTextBox logView;
public Timer timer;
[STAThread]
public static void Main()
{
Application app = new Application();
Test test = new Test();
test.logView = new LogTextBox(200);
test.timer = new Timer(200);
test.timer.Elapsed += new ElapsedEventHandler(test.timer_Elapsed);
test.timer.Start();
app.Run(main);
}
int line = 0;
void timer_Elapsed(object sender, ElapsedEventArgs e)
{
logView.Log(GetMessage(line++));
}
private string GetMessage(int line)
{
StringBuilder builder = new StringBuilder();
for (int i = 0; i < 1000; i++)
builder.Append(line + " ");
builder.Append('\n');
return builder.ToString();
}
}
在上述配置中,语句 3 执行 10 次平均需要 200 毫秒。如果将语句 2 注释掉,则语句 3 执行 10 次平均需要 0.1 毫秒。在这两种情况下,Log 方法的其他部分平均需要 10 毫秒。语句 1 和语句 2 的执行时间很小,并不重要。我使用高分辨率秒表进行测量。
为什么更新 Text 属性时 ScrollToEnd 需要这么长时间?ScrollToEnd(语句 3)的执行时间与 Text 属性的大小成正比,因为如果在 LogTextBox 构造函数中将 maxMessageCount 设置为 500,则需要 500 毫秒。我必须通过删除第一条消息来更新文本以限制使用的内存,但我还没有找到任何其他方式。有没有其他方法可以删除第一条消息?
编辑:
我按照建议尝试了 AvalonEdit,并从 TextEditor 而不是 TextBox 派生。我不必更改代码,因为方法名称相同。ScrollToEnd(Statement 3) 在相同的测试配置下平均花费 0.02 毫秒,并且无论 Text 属性的大小如何,它都保持不变。结果,我的性能问题得到了解决,我将使用 AvalonEdit。我给了 Jacob 赏金,因为他首先建议了 AvalonEdit。
TextBoxBase.ScrolltoEnd 调用 UpdateLayout(如下所示,使用 Reflector),我猜这就是它性能不佳的原因,而 AvalonEdit 的 TextEditor.ScrollToEnd 只调用 ScrollViewer.ScrollToEnd。
public void ScrollToEnd()
{
if (this.ScrollViewer != null)
{
base.UpdateLayout();
this.ScrollViewer.ScrollToEnd();
}
}