检查RichTextBox滚动条拇指是否在滚动条底部

3
我找到了这个链接WPF_Example,但它是用WPF编写的。我不会在WPF中编程,我正在使用Windows Forms,没有真正的理由想嵌入一个WPF RichTextBox到我的应用程序中,只是为了得到我需要的答案。
是否有一种方法,使用WindowsForms(而非WPF),来确定RichTextBox滚动条拇指是否位于滚动条底部?
其目的是允许我们的用户在RTF框中查看聊天记录时向上滚动,当添加文本时,如果他们向上滚动,则不要向下滚动。 想象一下mIRC如何处理聊天; 如果您在聊天框底部,文本将自动滚动到视图中; 如果您向上移动一行,文本将添加而无需滚动。
我需要复制这个功能。 我确实在SO上找到了这个链接:List_ViewScroll,但我不确定它是否适用于此情况。
任何帮助都将不胜感激 :)
RESOLUTION
使用这个类,我能够让它工作。 非常感谢下面的人指出并澄清了一些细节:
internal class Scrollinfo
{
    public const uint ObjidVscroll = 0xFFFFFFFB;

    [DllImport("user32.dll", SetLastError = true, EntryPoint = "GetScrollBarInfo")]
    private static extern int GetScrollBarInfo(IntPtr hWnd,
                                               uint idObject,
                                               ref Scrollbarinfo psbi);

    internal static bool CheckBottom(RichTextBox rtb)
    {


        var info = new Scrollbarinfo();
        info.CbSize = Marshal.SizeOf(info);

        var res = GetScrollBarInfo(rtb.Handle,
                                   ObjidVscroll,
                                   ref info);

        var isAtBottom = info.XyThumbBottom > (info.RcScrollBar.Bottom - info.RcScrollBar.Top - (info.DxyLineButton*2));
        return isAtBottom;
    }
}

public struct Scrollbarinfo
{
    public int CbSize; 
    public Rect RcScrollBar;
    public int DxyLineButton;
    public int XyThumbTop;
    public int XyThumbBottom;
    public int Reserved;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
    public int[] Rgstate;
}

public struct Rect
{
    public int Left;
    public int Top;
    public int Right;
    public int Bottom;
}       

请不要在问题中回答自己的问题,而是在您自己的答案中回答。 - CharlesB
我总是在我的问题中提供解决方案。抱歉:(这只是我的做法,这样其他人就可以看到已经完成的工作。有时候解决方案并不总是与其他人的答案完全一致。而且回答自己的问题可能会被视为...愚蠢。因此,在这里提供详细信息=对未来搜索的好信息。如果你想的话,你可以标记我,我不介意。但我觉得我在这里没有错。谢谢! - user674311
这并不傻,这就是stackoverflow的工作方式。将您的解决方案作为答案提供给这个问题更加合理,如果您将其标记为已接受,则会显示在答案顶部。如果您的答案得到了赞同,您还可以获得声望。 - CharlesB
当然,这样做的唯一缺点是,每次您回答自己的问题时都会失去声誉(现在,每次这样做时,声誉降低2分),有点烦人。 - user674311
将自己的问题作为答案回答是该网站的工作方式,也是每个人使用它的方式,因此您可以采取不同的行动,但这不是社区的工作方式。 - CharlesB
2个回答

5

所以,回答这个问题并不是非常复杂,但相当冗长。关键在于Win32 API函数GetScrollBarInfo,从C#中调用它相当容易。您需要在窗体中使用以下定义来进行调用...

[DllImport("user32.dll", SetLastError = true, EntryPoint = "GetScrollBarInfo")]
private static extern int GetScrollBarInfo(IntPtr hWnd,
    uint idObject, ref SCROLLBARINFO psbi);

public struct SCROLLBARINFO {
    public int cbSize;
    public RECT rcScrollBar;
    public int dxyLineButton;
    public int xyThumbTop;
    public int xyThumbBottom;
    public int reserved;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
    public int[] rgstate;
}

public struct RECT {
    public int Left;
    public int Top;
    public int Right;
    public int Bottom;
}        

为了测试GetScrollBarInfo函数,可以创建一个包含RichTextBox和按钮的表单。在按钮的点击事件中,进行以下调用(假设你的RichTextBox名为“richTextBox1”)...
uint OBJID_VSCROLL = 0xFFFFFFFB;

SCROLLBARINFO info = new SCROLLBARINFO();
info.cbSize = Marshal.SizeOf(info);

int res = GetScrollBarInfo(richTextBox1.Handle, OBJID_VSCROLL, ref info);
bool isAtBottom = info.xyThumbBottom >
    (info.rcScrollBar.Bottom - info.rcScrollBar.Top - 20);

电话结束后,一个简单的公式可以确定滚动条拇指是否在底部。 实质上,info.rcScrollBar.Bottominfo.rcScrollBar.Top是屏幕上的位置,它们之间的差异将告诉您滚动条的大小,无论其在屏幕上的位置如何。 同时,info.xyThumbBottom标记了拇指按钮底部的位置。 "20"基本上是猜测滚动条向下箭头的大小。 你看,拇指按钮的底部实际上永远不会完全到达滚动条的底部(这就是差异给你的东西),因此你必须再减去额外的量以适应向下按钮。 鉴于用户配置的不同,这有点不稳定,但这应该足以让您开始。


我认为这是一个优雅的解决方案,只是找到那个小框的大小可能会成为一个问题,因为我必须在其他具有不同DPI/分辨率的机器上运行它。但是,这基本上就是另一篇帖子中的内容,只是更加定制化,所以我会使用它。非常感谢 :) - user674311
所以我继续调整它的使用:var isAtBottom = info.XyThumbBottom > (info.RcScrollBar.Bottom - info.RcScrollBar.Top - (info.DxyLineButton*2));,看起来效果很不错。非常感谢你。 - user674311
不错的发现。看看这个在不同机器上的表现如何会很有趣。同时,我想到了一个长期重用的好方法,那就是创建一个名为 IsScrolledDown 的扩展方法。对于 RichTextBox 对象以及其他可滚动控件来说,这都是相当简单的事情。 - Aron Boyette
在我的上述类中,我将 internal static bool CheckBottom(RichTextBox rtb) 更改为 internal static bool CheckBottom(Control ctrl),并且它似乎适用于许多项目。这是个好主意,到目前为止,已经有50多人在使用它,并且似乎运行良好。非常感谢您对此的澄清 :) - user674311
变成51吧,我也在用它。我想到了一个可以与我正在开发的东西搭配使用的用途。 - Aron Boyette
不幸的是,这对于我50多个人来说有效,但大约3-5%的时间,它无法正确滚动;因此,它不是一种非常可靠的方法,特别是当考虑到最终将有数百人使用它,而该错误率可能相当高时。我希望C#中的RTF控件支持类似WPF中的scrollToEND(),或者如果有第三方控件具有该功能,我会有些迷茫:( - user674311

1

我的朋友(资深程序员)给了我这个解决方案,它完美地工作了(比多年前提供的解决方案更好):

var isAtBottom = rt.GetPositionFromCharIndex(rt.Text.Length).Y < rt.Height;

(注:该代码是用于判断文本框是否滚动到底部的)

这个解决方案可行且比被接受的方案简单得多。 - Renato
这个解决方案对于我在TextBox上起作用,但实际上应该是rt.Text.Length - 1 - Alberto Del Villano

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