【问题标题】:Obtain scroll bar 'position' in RichTextBox with scroll bars disabled在禁用滚动条的情况下获取 RichTextBox 中的滚动条“位置”
【发布时间】:2014-05-10 13:17:21
【问题描述】:

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

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

【问题讨论】:

    标签: c# winapi scroll scrollbar richtextbox


    【解决方案1】:

    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() 版本(因为我已经有了互操作版本的那些)。我不能怪你提到 XY 问题。你是对的,我的问题暗示了一个更深层次的问题,但如果你知道它是什么,你会跑一英里(这是它会打开的巨型兔子洞大小的 1/10,这要归功于怪癖RTB)。 (提示:垂直同步两个RTB随着RTF内容的变化,保持光标在同一位置,都没有闪烁)。
    • 那是trivial to do。隐藏意图总是错误的。
    • 我想我可能在很久以前就看到了,但这只是整个问题的一小部分,因为它的复杂性和 RTB 的故障。再次,我真的怀疑你想知道所涉及的内容。我研究了几天,甚至几周来研究这个问题。也许有一天我会重新审视这个问题,但现在,user3449857 在这个 Q 中的回答(或者你可能的回答)是一种享受。即使你确实提供了帮助,这也意味着我的代码需要大量返工。 (旁注,哇,你也做出了这个答案,难怪你的代表这么高!)。
    【解决方案2】:

    我想你会发现一些有趣的东西here。它是.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);。也可以吗?
    • 是的,WM_USER 和 DllImport 都可以。但是为什么你仍然得到零?我在发布之前测试了这段代码,对我来说很好。如果我能看到你的代码,那就太好了。您还必须发布ptr(在答案中添加代码)。
    • 我的错。我不小心在 DllImport 位中有EntryPoint = "PostMessageA"。我以为我之前删除了它,但也许其他地方出了问题,所以我取消了那个删除然后忘记了它。无论如何,赏金是你的 - 非常感谢你! :D
    • 我想我之前不小心忘记给你赏金了(很明显,打勾是不够的 - 哦!)。然而看起来它已经被社区授予了,但我仍然认为我应该欠债。但是,直到 4 月 22 日左右我都处于离线状态,并且在那之前我无法奖励这个新的 100 分(我现在能做到的最低限度),到那时我认为这个新的赏金也会过期。到那时,我可以奖励的最低分数将是 150/200 分。这对我来说很多,所以我可能会通过,但仅考虑到您的赏金已由社区提供。向您和所有相关人员道歉。
    猜你喜欢
    • 1970-01-01
    • 2011-04-08
    • 2023-01-10
    • 2013-12-11
    • 1970-01-01
    • 1970-01-01
    • 2012-03-18
    • 2014-02-23
    • 2017-11-08
    相关资源
    最近更新 更多