防止在RichTextBox中自动滚动

时间:2009-03-09 16:26:35

标签: c# winforms richtextbox

我有一个只使用RichTextBox控件实现的只读数据记录窗口。我希望能够禁用用户单击控件时发生的自动滚动,以便用户可以选择特定日志进行复制/粘贴操作或其他任何操作。但是,只要用户点击RichTextBox,它就会自动滚动到底部,这使得这很困难。

任何人都知道覆盖此行为的方法吗?

谢谢!

3 个答案:

答案 0 :(得分:11)

如果未隐藏选择,RichTextBox控件将自动滚动到当前选择。 RichTextBox.AppendText()除了附加文本外,还修改当前选择,因此间接触发“自动滚动”行为。请注意,如果RichTextBox.HideSelection设置为true,则当控件未处于焦点时,将隐藏选择;这解释了您描述的行为,其中仅在用户单击控件时才会发生自动滚动。 (从而给予关注) 为防止这种情况,您需要在附加文本时执行以下操作:

  1. 备份初始选择
  2. 取消对焦控件
  3. 隐藏选择(通过Windows消息)
  4. AppendText通过
  5. 恢复初始选择
  6. 取消隐藏选择
  7. 重新对焦控件
  8. 您可能还想检查选择是否已经在文本末尾,如果是,则允许自动滚动行为 - 这实质上模拟了Visual Studio的“输出”窗口的行为。例如:

        [System.Runtime.InteropServices.DllImport("user32.dll")]
        static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, Int32 wParam, Int32 lParam);
        const int WM_USER = 0x400;
        const int EM_HIDESELECTION = WM_USER + 63;
    
        void OnAppend(string text)
        {
            bool focused = richTextBox1.Focused;
            //backup initial selection
            int selection = richTextBox1.SelectionStart;
            int length = richTextBox1.SelectionLength;
            //allow autoscroll if selection is at end of text
            bool autoscroll = (selection==richTextBox1.Text.Length);
    
            if (!autoscroll)
            {
                //shift focus from RichTextBox to some other control
                if (focused) textBox1.Focus();
                //hide selection
                SendMessage(richTextBox1.Handle, EM_HIDESELECTION, 1, 0);
            }
    
            richTextBox1.AppendText(text);
    
            if (!autoscroll)
            {
                //restore initial selection
                richTextBox1.SelectionStart = selection;
                richTextBox1.SelectionLength = length;
                //unhide selection
                SendMessage(richTextBox1.Handle, EM_HIDESELECTION, 0, 0);
                //restore focus to RichTextBox
                if(focused) richTextBox1.Focus();
            }
        }
    

答案 1 :(得分:6)

你可以看看做这样的事情:

[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern IntPtr LockWindowUpdate(IntPtr Handle);

然后在你的方法中附加日志数据(我在这里做了一些假设)你可能会做这样的事情:

LockWindowUpdate(this.Handle);
int pos = richTextBox1.SelectionStart;
int len = richTextBox1.SelectionLength;
richTextBox1.AppendText(yourText);
richTextBox1.SelectionStart = pos;
richTextBox1.SelectionLength = len;
LockWindowUpdate(IntPtr.Zero);

我做了一个带有计时器的测试应用程序,该计时器在richtextbox上执行追加并且它停止了滚动,因此我可以进行文本选择。它有一些位置问题,并不完美,但也许它会帮助你实现自己的解决方案。

一切顺利!

答案 2 :(得分:1)

SytS的解决方案存在一个问题,当某些文本被“追加”时,滚动条会移动,以便选择转到面板的顶部。 解决方案是使用以下方法保存/恢复滚动位置:

    [System.Runtime.InteropServices.DllImport("User32.dll")]
    extern static int GetScrollPos(IntPtr hWnd, int nBar);

    [System.Runtime.InteropServices.DllImport("user32.dll")]
    static extern int SetScrollPos(IntPtr hWnd, int nBar, int nPos, bool bRedraw);

This solution对我来说更完整。