In one of my projects I needed a similar solution where I used a RichTextBox to display logs. The problem is easier to solve if we update the requirements a tiny little bit:
- If the
scrollbar cursor caret is in the bottom: display the latest text in the bottom. - If the
scrollbar cursor caret is not in the bottom: the text displayed is frozen, allowing the user to read this text. If he moves the scrollbar cursor caret to the bottom it continues displaying the latest text.
Meaning that you have to click or navigate by the keyboard to the position that you want to fix, rather than just scrolling up/down. And if you navigate back to the bottom (either by clicking or by pressing Ctrl+End), then you can see the latest updates again.
If you can live with these requirements, then here is a possible solution:
private void AppendText(string text, Color color) { int len = _richTextBox.TextLength; // empty text box: trivial case if (len == 0) { _richTextBox.ForeColor = color; _richTextBox.Text = message; _richTextBox.SelectionStart = message.Length; return; } // Saving the original position of the cursor. // If it's not at the very end, then it must be reset after appending the text. var selStart = _richTextBox.SelectionStart; var selLength = _richTextBox.SelectionLength; var resetSelection = selStart != len; // Appending the text with the specified color at the bottom _richTextBox.SelectionStart = len; _richTextBox.SelectionColor = color; _richTextBox.AppendText(message); // Optional extra: removing lines from the top to prevent growing indefinitely // (RichTextBox can be really slow with more then 10K lines) int lines = _richTextBox.GetLineFromCharIndex(len = _richTextBox.TextLength); if (lines > MaxLines) { int removeLen = _richTextBox.GetFirstCharIndexFromLine(lines - MaxLines); _richTextBox.Select(0, removeLen); selStart -= removeLen; len -= removeLen; _richTextBox.ReadOnly = false; _richTextBox.SelectedText = String.Empty; _richTextBox.ReadOnly = true; if (selStart < 0) resetSelection = false; } // The key part: resetting the original position if (resetSelection) { _richTextBox.SelectionStart = selStart; _richTextBox.SelectionLength = selLength; } else _richTextBox.SelectionStart = len; }
IsScrollingboolean on Scroll_MouseDown and set it to false on Scroll_MouseUp. Then in the operation where youAppendTextcheck if the boolean is false and set the caret to the end andScrollBar.Value = ScrollBar.Maximum;