【问题标题】:TextBox.ScrollToEnd takes too longTextBox.ScrollToEnd 花费的时间太长
【发布时间】:2017-10-27 01:27:52
【问题描述】:

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

【问题讨论】:

    标签: c# wpf .net-4.0 textbox


    【解决方案1】:

    如果易于阅读是主要的文档消费场景。 然后你可以尝试使用Flow Document

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

    已编辑:

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

    2. 您可以考虑使用不同的文本框控件。 这是一个完全从头开始的 SharpDevelop 的 Wpf 文本编辑器。它被称为 AvalonEdit,codeproject 上有一篇很好的文章: http://www.codeproject.com/KB/edit/AvalonEdit.aspx 看来他对大内容做了优化。

    【讨论】:

    • FlowDocument 不是从 TextBoxBase 派生的。支持查看 FlowDocument 的控件有 4 个:FlowDocumentReader、FlowDocumentPageViewer、RichTextBox 和 FlowDocumentScrollViewer。只有 RichTextBox 派生自 TextBoxBase 并具有 ScrollToEnd 方法,但我怀疑它是否会提供更好的性能,因为它是比 TextBox 更重的控件。
    • @AlpHancıoğlu:啊!,我的错!同意,FlowDocument 不是从 TextBoxBase 继承的。更新我的答案.. 谢谢!
    【解决方案2】:

    您是否尝试过从

    更改语句 2
           //statement 2
           Text = text + message + '\n';
    

    AppendText(message);
    AppendText('\n');
    

    ?

    这只是一个猜测,但由于所写的语句 2 每次都分配一个新的字符串对象,垃圾收集器可能会开始释放旧的字符串。我相信 AppendText 的实现方式可以避免中间分配。

    【讨论】:

    • 如果我不必从一开始就删除消息,我会这样做。这就是为什么我称之为'string text = Text.Remove(0, messageLengths[0]);'在语句 2 之前。
    【解决方案3】:

    您为什么不考虑使用不同的文本框控件?

    你可以试试AvalonEdit,它支持文本高亮,你可以在这里阅读http://www.codeproject.com/KB/edit/AvalonEdit.aspx

    似乎比原生 WPF 富文本控件更快。

    【讨论】:

      【解决方案4】:

      我发现logbox.ScrollToVerticalOffset(double.MaxValue);logbox.ScrollToEnd(); 快1/3。可能是因为它避免了对UpdateLayout()的内部调用。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-12-06
        • 1970-01-01
        • 1970-01-01
        • 2014-02-16
        • 1970-01-01
        • 1970-01-01
        • 2016-12-03
        相关资源
        最近更新 更多