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

本文关键字:文本 滚动 何防止 追加 | 更新日期: 2023-09-27 18:31:11

我有一个带有垂直滚动条的多行文本框,用于记录实时处理的数据。目前,每当textBox.AppendText()添加新行时,文本框都会滚动到底部,以便您可以看到最后一个条目,这很棒。但是我有一个复选框来指示是否允许文本框自动滚动。有什么办法可以做到这一点吗?

注意:

  • 我想使用 TextBox,因为添加的文本具有多行和空格对齐,因此与列表框或列表视图一起使用并不简单。
  • 我试图按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所建议的那样,我还认为解决此问题的方法是在附加文本之前确定文本框中显示的当前文本中第一个字符的位置,然后恢复文本。但是如何做到这一点呢?我试图像这样记录当前的光标位置,但它没有帮助:

int selpoint = txtLog.SelectionStart;
txtLog.AppendText(Environment.NewLine);
txtLog.AppendText(text);
txtLog.SelectionStart = selpoint;

更新2(问题已解决):我找到了一个可以解决我在堆栈溢出上的问题的解决方案。我已经优化了他们的代码以适应我的情况,如下所示:

// 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 的源代码。

没有人有更直接的方法?

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

这看起来很简单,但我可能错过了一些东西。 如果"自动选中"为 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));
    }

你必须这样做,

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,但谷歌可能为你提供了答案。

所需的操作是:

  • 在将滚动条拖动到底部时打开自动滚动。
  • 在将折叠条拖动到其他任何位置时关闭自动滚动。

创建以下类

    public class AutoScrollTextBox : TextBox
    {
        protected override void OnInitialized(EventArgs e)
        {
            base.OnInitialized(e);
            VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
            HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled;
        }
        protected override void OnTextChanged(TextChangedEventArgs e)
        {
            bool isScrolledToEnd = VerticalOffset + ViewportHeight == ExtentHeight;
            base.OnTextChanged(e);
            CaretIndex = Text.Length;
            if (isScrolledToEnd)
            {
                ScrollToEnd();
            }                
        }
    }

并将 XML 中的文本框替换为 AutoScrollTextBox,并在内容到达显示时附加到 TextToDisplay 绑定

<local:AutoScrollTextBox Text="{Binding TextToDisplay }" />