我有一个带有垂直滚动条的多行文本框,用于记录实时处理的数据。目前,每当通过 textBox.AppendText()
添加新行时,文本框会自动滚动到底部以便查看最后一项内容,这非常好。但是,我有一个复选框来指示是否允许文本框自动滚动。有没有办法做到这一点呢?
注意:
- 我想使用文本框,因为添加的文本具有多行和空格对齐,所以使用 ListBox 或 ListView 不太简单。
- 我尝试通过
textBox.Text += text
添加新行,但文本框不停地滚动到顶部。
如果我们有解决方案,那么另一个问题是如何防止当用户使用滚动条在文本框中浏览其他位置时,文本框自动滚动而不影响文本框追加文本呢?
private void OnTextLog(string text)
{
if (chkAutoScroll.Checked)
{
// This always auto scrolls to the bottom.
txtLog.AppendText(Environment.NewLine);
txtLog.AppendText(text);
// This always auto scrolls to the top.
//txtLog.Text += Environment.NewLine + text;
}
else
{
// I want to append the text without scrolls right here.
}
}
更新 1:正如saggio所建议的那样,我也认为解决此问题的方法是确定在将文本追加到并在此之后恢复的 TextBox 中显示的当前文本中第一个字符的位置。但怎么做呢?我尝试记录当前光标位置,像这样,但它并没有帮助:
int selpoint = txtLog.SelectionStart;
txtLog.AppendText(Environment.NewLine);
txtLog.AppendText(text);
txtLog.SelectionStart = selpoint;
更新2 (问题已解决):我在 Stack Overflow 上找到了一个解决方案,可以解决我的问题。我已经优化了他们的代码以适应我的情况,如下:
// Constants for extern calls to various scrollbar functions
private const int SB_VERT = 0x1;
private const int WM_VSCROLL = 0x115;
private const int SB_THUMBPOSITION = 0x4;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int GetScrollPos(IntPtr hWnd, int nBar);
[DllImport("user32.dll")]
private static extern int SetScrollPos(IntPtr hWnd, int nBar, int nPos, bool bRedraw);
[DllImport("user32.dll")]
private static extern bool PostMessageA(IntPtr hWnd, int nBar, int wParam, int lParam);
[DllImport("user32.dll")]
private static extern bool GetScrollRange(IntPtr hWnd, int nBar, out int lpMinPos, out int lpMaxPos);
private void AppendTextToTextBox(TextBox textbox, string text, bool autoscroll)
{
int savedVpos = GetScrollPos(textbox.Handle, SB_VERT);
textbox.AppendText(text + Environment.NewLine);
if (autoscroll)
{
int VSmin, VSmax;
GetScrollRange(textbox.Handle, SB_VERT, out VSmin, out VSmax);
int sbOffset = (int)((textbox.ClientSize.Height - SystemInformation.HorizontalScrollBarHeight) / (textbox.Font.Height));
savedVpos = VSmax - sbOffset;
}
SetScrollPos(textbox.Handle, SB_VERT, savedVpos, true);
PostMessageA(textbox.Handle, WM_VSCROLL, SB_THUMBPOSITION + 0x10000 * savedVpos, 0);
}
private void OnTextLog(string text)
{
AppendTextToTextBox(txtLog.Text, Environment.NewLine + text, chkAutoScroll.Checked);
}
另一种方法:
private const int SB_VERT = 0x1;
private const int WM_VSCROLL = 0x115;
private const int SB_THUMBPOSITION = 0x4;
private const int SB_BOTTOM = 0x7;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int GetScrollPos(IntPtr hWnd, int nBar);
[DllImport("user32.dll")]
private static extern int SetScrollPos(IntPtr hWnd, int nBar, int nPos, bool bRedraw);
[DllImport("user32.dll")]
private static extern bool PostMessageA(IntPtr hWnd, int nBar, int wParam, int lParam);
private void AppendTextToTextBox(TextBox textbox, string text, bool autoscroll)
{
int savedVpos = GetScrollPos(textbox.Handle, SB_VERT);
textbox.AppendText(text + Environment.NewLine);
if (autoscroll)
{
PostMessageA(textbox.Handle, WM_VSCROLL, SB_BOTTOM, 0);
}
else
{
SetScrollPos(textbox.Handle, SB_VERT, savedVpos, true);
PostMessageA(textbox.Handle, WM_VSCROLL, SB_THUMBPOSITION + 0x10000 * savedVpos, 0);
}
}
我发布这些解决方案是为了那些遇到相似问题的人。感谢cgyDeveloper的源代码。
有没有更加简单直接的方法?
TextBox
追加文本之前记录光标位置,然后在追加文本后将其设置回该值,会怎样呢? - SaggiotextBox.SelectionStart
没有帮助吗?当你添加文本时,TextBox
是否具有焦点?如果没有,请尝试将textBox.HideSelection
属性设置为false
。 - Saggio