追加文本时如何防止文本框自动滚动? [英] How to prevent TextBox auto scrolls when append text?

查看:14
本文介绍了追加文本时如何防止文本框自动滚动?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a multi-line TextBox with a vertical scrollbar that logs data from a realtime process. Currently, whenever a new line is added by textBox.AppendText(), the TextBox scrolls to the bottom so you can see the last entry, this great. But I have a checkbox to decides when the TextBox is allowed to auto scrolls. Is there anyway to do this?

注意:

  • 我想使用 TextBox,因为添加的文本有多行并由空格格式化,因此与 ListBox 或 ListView 一起使用并不简单.
  • 我尝试通过 textBox.Text += text 添加新行,但 TextBox 总是滚动到顶部.
  • I want to uses the TextBox because the added text has multi-lines and formated by whitespaces so it's not simple to uses with a ListBox or a ListView.
  • I tried to add a new line by textBox.Text += text, but the TextBox always scrolls to the top.

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

If we have a solution to do that, then one more question is how to prevent that the TextBox auto scrolls when the user uses the scrollbar to view somewhere else in the TextBox while the TextBox appends 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 中显示的位置,然后再附加文本并在之后恢复它.但是如何做到这一点?我试图像这样记录当前光标位置,但没有帮助:

Update 1: As saggio suggests, I also think the solution to this problem is to determine the position of the first character in the current text that is displayed in the TextBox before appending text and restoring it after that. But how to do this? I tried to record the current cursor position like this, but it did not help:

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

更新 2 (问题已解决):我找到了一个 solution 可以解决我在 Stack Overflow 上的问题.我已经优化了他们的代码以适应我的问题,如下所示:

Update 2 (the issue was resolved): I found a solution that can solve my issue here on Stack Overflow. I have optimized their code to suit my issue as follows:

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

有人有更简单的方法吗?

Does anyone have a simpler way?

推荐答案

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

This seems pretty straight forward but I may be missing something. Use append text to scroll to the position if Autochecked is true and just add the text if you do not wish to scroll.

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

Update...I was missing something. You want to set the selection point and then scroll to the caret. See below.

    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));
    }

这篇关于追加文本时如何防止文本框自动滚动?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆