文本框.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();
    }
}  

文本框.ScrollToEnd耗时太长

方便阅读是主要的文档消费场景。然后你可以尝试使用流程文档。

注意:这个FlowDocument的设计是为了优化查看和可读性。RichText框表示在FlowDocument对象上操作的富编辑控件。

编辑:

  1. 如果你不希望在你的应用程序中有超过一万个搜索结果,一个TextBlock控件或只读多行文本框就足够了。TextBox类有一个AppendText()方法,它应该足够快。你不需要删除第一条消息

  2. 您可以考虑使用不同的文本框控件。这是一个完全从零开始为夏普开发的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()