防止RichTextBox自动滚动

12

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

有人知道如何覆盖此行为吗?

谢谢!

3个回答

13
RichTextBox控件会自动滚动到当前选定内容,如果该选定内容没有被隐藏。RichTextBox.AppendText()不仅追加文本,还会修改当前选定内容,从而间接触发“自动滚动”行为。请注意,如果将RichTextBox.HideSelection设置为true,则在控件不处于焦点时,选择内容将被隐藏;这解释了您所描述的行为,即仅当用户在控件中单击时(从而使其获得焦点),才会出现自动滚动。
要防止这种情况,请在追加文本时执行以下操作:
  1. 备份初始选定内容
  2. 取消控件的焦点
  3. 隐藏选定内容(通过Windows消息)
  4. 追加文本
  5. 恢复初始选定内容
  6. 取消隐藏选定内容
  7. 重新将焦点聚焦到控件
您还可以检查选定内容是否已在文本末尾,并在这种情况下允许自动滚动行为 - 这基本上模拟了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();
        }
    }

你的解决方案太棒了!我尝试了很多其他方法来防止我的RichTextBox在进行格式更改后滚动,但这是唯一有效的方法。起初它似乎很复杂,但它确实起作用 :) - Eugenio De Hoyos
好的,在调用此方法之前,您需要将焦点移动到另一个元素(控件)。 - Bokili Production

5
你可以考虑像这样做:

你可以尝试以下方法:

[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上进行附加,并停止了滚动,以便我可以进行文本选择。它有一些位置问题,不是完美的,但也许它会帮助你朝着自己的解决方案迈进。

祝一切顺利!


LockWindowUpdate会锁定甚至是按键,因此无法输入。 - bansi
-1 是因为这是滥用 LockWindowUpdate;它仅用于拖动操作,并且在现代Windows上根本不应该使用。 - Setsu

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

这个解决方案对我来说更加完整。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接