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