c# - 附加文本时如何防止文本框自动滚动?

标签 c# wpf winforms winapi textbox

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

注意:

  • 我想使用 TextBox,因为添加的文本具有多行和空格对齐,因此与 ListBox 或 ListView 一起使用并不简单。
  • 我尝试通过 textBox.Text += text 添加新行,但 TextBox 不断滚动到顶部。

如果我们有解决方案,那么还有一个问题是当用户使用滚动条查看 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 (问题已解决):我发现了一个 solution可以在 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的源代码。

有没有人有更直接的方法?

最佳答案

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

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

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

关于c# - 附加文本时如何防止文本框自动滚动?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18793548/

相关文章:

wpf - 我的 URI 必须这么长和明确吗?

c# - 如何在 C# 中使用 mshtml 设置文档第一个表单中第一个输入类型文本的值?

c# - Kendo datagrid 未将排序字段参数发送到服务器(MVC 中的 Controller )

c# - 为什么 List<T>.Sort 使用 Comparer<int>.Default 比等效的自定义比较器快两倍以上?

WPF 编译开始因交互和交互性而失败

winforms - 防止多个表单实例

c# - 如何获得证书 key 大小

c# - 如何从 C# 应用程序向树莓派发送终端命令

c# - 为什么没有触发DataGridView的CellEndEdit事件

vb.net - Windows应用程序中ComboBox重置选择ComboBox的文本