ログメッセージを表示するための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)の実行時間は、LogTextBoxコンストラクターでmaxMessageCountが500に設定されている場合、500ミリ秒かかるため、Textプロパティのサイズに比例します。使用済みメモリを制限するために最初のメッセージを削除してテキストを更新する必要がありますが、他の方法は見つかりませんでした。最初のメッセージを削除する他の方法はありますか?
編集:
提案され、TextBoxではなくTextEditorから派生したAvalonEditを試しました。メソッド名が同じなので、コードを変更する必要はありませんでした。ScrollToEnd(ステートメント3)は、同じテスト構成で平均0.02ミリ秒かかり、Textプロパティのサイズに関係なく一定に保たれます。その結果、パフォーマンスの問題が解決され、AvalonEditを使用します。ジェイコブが最初にアバロンエディットを提案したので、私はジェイコブに賞金を与えました。
TextBoxBase.ScrolltoEndはUpdateLayout(Reflectorを使用して以下に表示)を呼び出します。これがパフォーマンスの低下の理由だと思いますが、AvalonEditのTextEditor.ScrollToEndはScrollViewer.ScrollToEndを呼び出すだけです。
public void ScrollToEnd()
{
if (this.ScrollViewer != null)
{
base.UpdateLayout();
this.ScrollViewer.ScrollToEnd();
}
}