添加行时保持TextBox滚动位置

时间:2012-04-16 13:49:26

标签: c# .net winforms textbox

在我的WinForm应用程序中,我有一个多行TextBox控件(uiResults),用于在处理大量项目时报告进度。使用AppendText非常适合在每次更新时自动滚动到底部,但如果用户向后滚动以读取一些旧数据,我需要关闭自动滚动。如果可能的话,我宁愿远离P / Invoke电话。

是否可以检测用户是否在不使用P / Invoke的情况下向后滚动?现在,我只检查SelectionStart哪个有效,但要求用户从文本框的末尾移动插入符号以停止自动滚动:

if(uiResults.SelectionStart == uiResults.Text.Length)
{
  uiResults.AppendText(result + Environment.NewLine);
}

我的主要问题是,当使用Text属性追加字符串时,文本框会滚动到开头。我试图通过存储插入位置并在更新后重置并滚动到它来解决这个问题,但这会导致当前行移动到底部(当然,因为ScrollToCaret滚动的距离不超过将插入符号移入视图的必要距离)。

[Continued from above]
else
{
  int pos = uiResults.SelectionStart;
  int len = uiResults.SelectionLength;
  uiResults.Text += result + Environment.NewLine;
  uiResults.SelectionStart = pos;
  uiResults.SelectionLength = len;
  uiResults.ScrollToCaret();
}

3 个答案:

答案 0 :(得分:3)

Auto-scrolling text box uses more memory than expected

问题中的代码完全符合您的要求。添加了文本,但只有滚动条位于最底部时才会滚动。

答案 1 :(得分:0)

您是否对另一种方法持开放态度,因为您必然会遇到麻烦,解决方案将变得复杂(您希望避免使用pinvoke等)。例如。假设您找到了“检测用户是否已向后滚动”的方法,并且您停止滚动到底部。但在阅读该行后,用户可能希望滚动到底部功能恢复。那么为什么不给用户一种控制自动滚动的方法呢。我就是这样做的......

使用RichTextBox显示数据,使用Checkbox控制AutoScrolling,然后您的代码可能是这样的:

        richTextBox1.AppendText(result + Environment.NewLine);
        if (checkBoxAutoScroll.Checked)
        {
            richTextBox1.SelectionStart = richTextBox1.Text.Length;
            richTextBox1.ScrollToCaret(); 
        }

默认情况下,RichTextBox不会自动滚动到AppendText的底部,因此第一行始终可见(而不是新添加的行)。但是如果用户选中了这个名为AutoScroll的复选框,我们的代码会将richtextbox滚动到显示新行的底部。如果用户想要手动滚动以读取一行,他首先需要取消选中该复选框。

答案 2 :(得分:0)

我遇到了同样的问题。 最后,我做了一个简单的方法。 (对不起,我英语不好。)

关键点是使用GetCharIndexFromPosition方法获取第一个显示的char索引。

//Get current infomation
int selStart = textBox.SelectionStart;
int selLen = textBox.SelectionLength;
int firstDispIndex = textBox.GetCharIndexFromPosition(new Point(3, 3));

//Append Text
textBox.AppendText(...);

//Scroll to original displayed position
textBox.Select(firstDispIndex, 0);
text.ScrolltoCaret();

//Restore original Selection
textBox.Select(selStart, selLen);

并且,如果文本框不流畅,请使用此扩展名。 添加文本之前调用textBox.Suspend(),添加文本之后调用textBox.Resume()。

namespace System.Windows.Forms
{
    public static class ControlExtensions
    {
        [System.Runtime.InteropServices.DllImport("user32.dll")]
        public static extern bool LockWindowUpdate(IntPtr hWndLock);

        public static void Suspend(this Control control)
        {
            LockWindowUpdate(control.Handle);
        }

        public static void Resume(this Control control)
        {
            LockWindowUpdate(IntPtr.Zero);
        }

    }
}

希望这会对您有所帮助。 谢谢〜