追加文本时如何防止文本框自动滚动?

Posted

技术标签:

【中文标题】追加文本时如何防止文本框自动滚动?【英文标题】:How to prevent TextBox auto scrolls when append text? 【发布时间】:2013-09-18 13:38:23 【问题描述】:

我有一个带有垂直滚动条的多行文本框,用于记录来自实时进程的数据。目前,每当textBox.AppendText() 添加新行时,TextBox 都会滚动到底部,这样您就可以看到最后一个条目,这很棒。但是我有一个复选框来决定何时允许 TextBox 自动滚动。有没有办法做到这一点?

注意:

我想使用 TextBox,因为添加的文本有多行并由空格格式化,因此与 ListBox 或 ListView 一起使用并不简单。 我尝试通过textBox.Text += text 添加新行,但文本框总是滚动到顶部。

如果我们有解决方案,那么另一个问题是如何防止当用户使用滚动条查看 TextBox 中的其他位置而 TextBox 附加文本时 TextBox 自动滚动?

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 上找到了可以解决我的问题的 solution。我已经优化了他们的代码以适应我的问题,如下所示:

// 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 中,然后再将其设置回该值? 如何记录当前光标位置? textbox.SelectionStart 属性没有帮助:( textBox.SelectionStart 没有帮助是什么意思? TextBox 在附加文本时是否有焦点?如果没有,请尝试将 textBox.HideSelection 属性设置为 false 我尝试在附加文本之前设置textbox.HideSelection = false 并记录SelectionStart,然后按照您的建议再次恢复旧的SelectionStart 值。但它仍然没有帮助。 这是WinForms还是WPF等? 【参考方案1】:

这看起来很简单,但我可能遗漏了一些东西。如果 Autochecked 为 true,则使用附加文本滚动到该位置,如果您不想滚动,则只需添加文本。

更新...我错过了一些东西。您要设置选择点,然后滚动到插入符号。见下文。

    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
    
        int caretPos = txtLog.Text.Length;
        txtLog.Text += Environment.NewLine + text;
        txtLog.Select(caretPos, 0);            
        txtLog.ScrollToLine(txtLog.GetLineIndexFromCharacterIndex(caretPos));
    

【讨论】:

在没有 ScrollToCaret() 的情况下看起来可以这样做:***.com/questions/13621549/… @NicolasLouisGuillemot 更新以反映对文本框的正确 wpf 调用。赞同你的评论 是的,这段代码在 WPF 中很有用。感谢您的贡献。 最终发现这种方法效果最好:double offset = txtLog.VerticalOffset; txtLog.Text += Environment.NewLine + 文本; txtLog.ScrollToVerticalOffset(offset); @NicolasLouisGuillemot 解释一下为什么 Offset 方法更好可能对后面的人有所帮助。【参考方案2】:

你必须这样做,

textBox1.AppendText("Your text here");
// this selects the index zero as the location of your caret
textBox1.Select(0, 0);
// Scrolls to the caret :)
textBox1.ScrollToCaret();

在 VS2010 c# Winforms 上测试和工作,我不知道 WPF,但谷歌可能有你的答案。

【讨论】:

您的代码将始终使文本框滚动到顶部。这不是我想要的。我们在这里讨论的问题是让文本框始终停留在当前位置,添加新文本时不要滚动。

以上是关于追加文本时如何防止文本框自动滚动?的主要内容,如果未能解决你的问题,请参考以下文章

Java / Swing:JScrollPane中的JTextArea,如何防止自动滚动?

Kivy Scrollview 自动滚动到新文本。防止向上滚动

如何使用自动滚动制作水平滚动文本视图?

TextBox.AppendText() 不自动滚动

自动滚动到由 backgroundworker 更新的多行文本框的底部

当焦点和键盘隐藏文本字段时滚动到文本字段