TextBox.ScrollToEnd需要太长时间

时间:2013-03-14 23:09:43

标签: c# wpf .net-4.0 textbox

我有一个用于显示日志消息的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();
    }
}  

4 个答案:

答案 0 :(得分:0)

如果易读性是主要文档消费方案。 然后你可以尝试使用Flow Document

注意:此FlowDocument旨在优化查看和可读性。 RichText框表示对FlowDocument对象进行操作的丰富编辑控件。

编辑:

  1. 如果您的应用程序中不会有超过一万个搜索结果,那么TextBlock控件或只读多行TextBox就足够了。 TextBox类有一个AppendText()方法,它应该足够快。您不必删除第一条消息。

  2. 您可以考虑使用其他文本框控件。 这是一个完全从头开始的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()的内部调用。