在禁用滚动条的RichTextBox中获取滚动条的位置

5

在我之前的帖子中,展示了如何获取RichTextbox中水平或垂直滚动条的位置。但是,这些仅适用于启用了滚动条的情况。如果您将滚动条设置为None(通过richTextBox1.ScrollBars = RichTextBoxScrollBars.None;),则仍然可以向下(如果禁用WordWrap,则还可以向右)滚动到框的底部。但是,像我发布的链接中显示的getVerticalScroll()getHorizontalScroll()方法现在只返回0。它们似乎需要“看到”滚动条才能真正起作用。

那么,在禁用滚动条的情况下,我该如何获取(和设置)“滚动位置”?

2个回答

6

RichTextBox类有几个辅助方法,可以让您发现窗口内文本的位置。这正是您需要在此处使用的,因为您不再直接从滚动条获得反馈。

您可以使用RichTextBox.GetCharIndexFromPosition()查找显示为第一可见行的行,以查找第一个可见字符,然后使用GetLineFromCharIndex()来确定包含该字符的行:

    public static double GetVerticalScrollPos(RichTextBox box) {
        int index = box.GetCharIndexFromPosition(new Point(1, 1));
        int topline = box.GetLineFromCharIndex(index);
        int lines = box.Lines.Length;
        return lines == 0 ? 0 : 100.0 * topline / lines; 
    }

水平滚动条的位置可以通过查找插入符当前所在行来确定。然后将该行的开头映射回一个位置。
    public static double GetHorizontalScrollPos(RichTextBox box) {
        int index = box.SelectionStart;
        int line = box.GetLineFromCharIndex(index);
        int start = box.GetFirstCharIndexFromLine(line);
        Point pos = box.GetPositionFromCharIndex(start);
        return 100 * (1 - pos.X) / box.ClientSize.Width;
    }

你可能需要重新定义100%的含义,因为你的原始问题没有给出任何指导。
这个问题有一个XY Problem的方面,而且似乎你真正想做的是用更大的滚动条替换它们。不要忽视愚蠢简单的解决方案,你可以通过正确地放置你的滚动条使它们重叠并隐藏现有的滚动条。你只需要改变边框,让它不那么明显。

非常感谢您的回答。我可能会暂时坚持另一个答案(在看到您的答案之前,我已经授予了赏金),除非您也添加Set()版本(因为我已经拥有Interop版本的那些)。我不能责怪您提到XY问题。您是对的,我的问题暗示着更深层次的问题,但如果您知道它是什么,您会逃之夭夭的(这将打开一个巨大的兔子洞,大小只有1/10,由于RTB的怪癖,可以垂直同步两个具有不断变化的RTF内容的RTB,保持光标在相同位置,而无需闪烁)。(提示:) - Dan W
这很容易做到。隐藏意图总是一个错误。 - Hans Passant
我想我可能很久以前就看到了那个,但由于其复杂性和RTB的故障,它只是整个问题的一个小组成部分。再次强调,我真的怀疑你想知道其中涉及了什么。我为这个问题进行了数天甚至数周的研究。也许有一天我会重新审视这个问题,但现在,在这个Q中,user3449857的答案(或者你的答案)非常有效。即使你提供帮助,也意味着我的代码需要进行大量的重构。(顺便说一句,哇,你也回答了那个问题,难怪你的声望这么高!)。 - Dan W

5

我认为您会发现这里有一些有趣的内容。它是 .Net 中在 RichTextBox 后面使用的 RichEdit 的描述。

另外,这是您问题的解决方案:

var ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(POINT)));
Marshal.StructureToPtr(new POINT(), ptr, false);
SendMessage(this.richTextBox1.Handle, EM_GETSCROLLPOS, IntPtr.Zero, ptr);
var point = (POINT)Marshal.PtrToStructure(ptr, typeof(POINT));
Marshal.FreeHGlobal(ptr);

地点:

EM_GETSCROLLPOS = WM_USER + 221

以下是来自 pinvoke.netPOINT结构。


谢谢,这看起来很有前途,但是即使启用了滚动条,我仍然在返回point.X或point.Y后得到零。我将WM_USER更改为1024 - 这样可以吗?我还添加了以下行:[DllImport("User32.Dll"]static extern bool SendMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);。这也可以吗? - Dan W
是的,WM_USER和DllImport都没问题。但是为什么你仍然得到零呢?在发布之前我已经测试过这段代码,对我来说运行良好。如果我能看到你的代码就太好了。另外,你必须释放ptr(添加到答案的代码)。 - user3449857
我的错。我在 DllImport 部分意外地设置了 EntryPoint = "PostMessageA"。我以为我早就删除了它,但可能其他地方出了问题,所以我撤销了那个删除操作,然后后来忘记了它。无论如何,赏金归你了 - 非常感谢!:D - Dan W
我想我可能不小心忘记在之前给你颁发赏金了(仅仅勾选是不够的 - 哎呀!)。然而,看起来社区已经授予了这个奖励,但我仍然认为我应该还清这笔债务。不过,我要到4月22日左右才能上线,届时我才能授予这个新的100分奖励(现在我能做的最少),但到那时这个新的赏金也会过期。到那时,我能授予的最低分数将是150/200分。对于我来说,这是很多的,所以我可能会放弃,但只考虑到您的赏金已经由社区提供。对您和所有相关人员表示歉意。 - Dan W

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