我有一个用于显示日志消息的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平均需要200毫秒执行10次。如果声明2被注释掉,声明3平均需要0.1毫秒才能执行10次。在这两种情况下,Log方法的其他部分平均需要10 ms。声明1和声明2的执行时间很短,并不重要。我使用高分辨率秒表进行测量。
为什么ScrollToEnd在Text属性更新时需要这么长时间? ScrollToEnd(语句3)的执行时间与Text属性的大小成比例,因为如果在LogTextBox构造函数中将maxMessageCount设置为500,则需要500ms。我必须通过删除第一条消息更新文本以限制使用的内存,我还没有找到任何其他方式。有没有其他方法可以删除第一条消息?
修改
我按照建议尝试了AvalonEdit,并从TextEditor而不是TextBox派生。我没有必要更改代码,因为方法名称相同。 ScrollToEnd(语句3)使用相同的测试配置平均需要0.02毫秒,并且无论Text属性的大小如何,它都保持不变。结果,我的性能问题得到解决,我将使用AvalonEdit。我首先向雅各布推荐AvalonEdit给予赏金。
TextBoxBase.ScrolltoEnd调用UpdateLayout(如下所示使用Reflector),我猜这是其性能不佳的原因,而AvalonEdit的TextEditor.ScrollToEnd只调用ScrollViewer.ScrollToEnd。
public void ScrollToEnd()
{
if (this.ScrollViewer != null)
{
base.UpdateLayout();
this.ScrollViewer.ScrollToEnd();
}
}
答案 0 :(得分:0)
如果易读性是主要文档消费方案。 然后你可以尝试使用Flow Document。
注意:此FlowDocument旨在优化查看和可读性。 RichText框表示对FlowDocument对象进行操作的丰富编辑控件。
编辑:
如果您的应用程序中不会有超过一万个搜索结果,那么TextBlock控件或只读多行TextBox就足够了。 TextBox类有一个AppendText()方法,它应该足够快。您不必删除第一条消息。
您可以考虑使用其他文本框控件。 这是一个完全从头开始的SharpDevelop的Wpf文本编辑器。它被称为AvalonEdit,一篇很好的文章在codeproject上: http://www.codeproject.com/KB/edit/AvalonEdit.aspx 他似乎已经对大内容进行了优化。
答案 1 :(得分:0)
您是否尝试过更改
中的语句2 //statement 2
Text = text + message + '\n';
到
AppendText(message);
AppendText('\n');
这只是一个猜测,但由于每次写入的语句2都会分配一个新的字符串对象,因此垃圾收集器可能会踢出来释放旧字符串。我相信AppendText的实现方式可以避免中间分配。
答案 2 :(得分:0)
为什么不考虑使用不同的文本框控件?
您可以尝试支持文字突出显示的AvalonEdit,您可以在此处阅读http://www.codeproject.com/KB/edit/AvalonEdit.aspx
本机WPF richtext控件似乎更快。
答案 3 :(得分:0)
我发现logbox.ScrollToVerticalOffset(double.MaxValue);
比logbox.ScrollToEnd();
快1/3。也许是因为它避免了对UpdateLayout()
的内部调用。