自定义 Windows 窗体滚动条

9

我在互联网上搜索了很久,但没有找到合适的答案。

在我的Windows Form应用程序中,我想要改变属于FlowLayoutPanel的滚动条的宽度。

由于Flow Layout Panel的内容比窗体大,所以滚动条是“自动”添加的。

根据我在网上找到的信息,这似乎有点棘手。

有没有什么解决办法呢?

谢谢!

3个回答

25

很抱歉,无法更改单个控件上显示的滚动条的宽度(尽管有一个系统范围的设置会影响所有应用程序中的所有滚动条)。

丑陋的事实是,低贱的滚动条控件比它看起来要复杂得多。基本上,FlowLayoutPanel上的滚动条是由Windows自身绘制的(而不是.NET Framework),因为在幕后为控件设置了WS_HSCROLL和/或WS_VSCROLL窗口样式。 FlowLayoutPanel不提供任何更改或修改这些内置滚动条如何绘制的工具。与WinForms中的其他更高级的修改不同,我们无法向控件的窗口过程发送此类消息。更糟糕的是,滚动条在FlowLayoutPanel的非客户区域中绘制,这意味着我们不能仅覆盖其Paint事件并处理自己绘制滚动条。

不幸的是,如果您真的想自定义滚动条,则必须隐藏内置滚动条并自行创建。 如果您愿意,这并不像听起来那么困难。 这篇文章在CodeProject上提供了一个很好的演示,介绍如何创建自己的可自定义外观的滚动条作为用户控件,并将其用作所选容器控件中的替代品。


非常感谢您的回答,Cody Gray!我会查阅您提供的文章。希望这能帮助其他需要相同信息的人。 - Anders
@Anders:我刚意识到这篇文章是用C#写的,而你的问题标签是VB.NET,但是在两者之间进行转换相对来说是比较简单的。如果你遇到了麻烦,可以尝试使用在线转换工具之一例如DeveloperFusion提供的工具。很高兴能帮到你! - Cody Gray
没问题 :) 如果我能得到它的逻辑,我会自己做的。你知道,不能懒惰! ;) 虽然谢谢你提醒我! - Anders

3

Cody Gray的回答是100%正确的,但我想添加更多有关此主题的参考资料。

背景

Windows Forms创建滚动条的方式是通过使用窗口样式WS_HSCROLLWS_VSCROLL。分别对应于启用给定HWND的水平和垂直滚动条。 HWND是到"窗口"的本地资源句柄,其在.NET语言中对应于Control

从Windows API角度思考,我们必须在创建HWND时设置窗口样式。这是通过调用CreateWindowCreateWindowExSetWindowLong来完成的。自然而然地,我们可以考虑使用P / Invoke来帮助我们,但这将是相当繁琐的,因为这意味着我们需要从头开始重新实现Windows Forms。

幸运的是,Windows Forms公开了一个属性CreateParams,该属性可以被覆盖以指定精确的窗口样式,以及其他Control创建参数。 .NET框架会使用此属性,以便在实例化Control时能够创建具有适当样式的HWND

自定义滚动条

替换Windows API滚动条的功能实际上比看起来要简单得多;然而,这并不是很明显(至少对我来说是这样,我必须查阅.NET源代码才能找到答案)。为了做到这一点,我们必须选择适当的Control作为继承对象,以创建我们自己的自定义ScrollableControl。如果我们观察System.Windows.Forms.ScrollableControl的源代码,我们会发现使用了以下样式:

CreateParams cp = base.CreateParams;
 
if (HScroll || HorizontalScroll.Visible) {
    cp.Style |= NativeMethods.WS_HSCROLL;
}
else {
    cp.Style &= (~NativeMethods.WS_HSCROLL);
}
if (VScroll || VerticalScroll.Visible) {
    cp.Style |= NativeMethods.WS_VSCROLL;
}
else {
    cp.Style &= (~NativeMethods.WS_VSCROLL);
}

简而言之,当我们从ScrollableControl扩展时,原生的水平和垂直滚动条是基于其内部逻辑启用的。我们可以访问ScrollableControl的窗口句柄,然后调用SetWindowLong来隐藏滚动条;但是,我们需要跟踪所有与Windows API交互的地方。实际上,有一个内部函数Control.UpdateStylesCore(),根据是否应显示滚动条来调用它。这个函数有效地重新应用了窗口样式,最好不要与它争执。更清晰的方法是避免使用Windows API,直接从Control扩展。然后我们可以提供任何所需的API。
这意味着我们需要重新实现:
  1. 根据鼠标滚轮事件更新滚动条。
  2. 根据单击自定义滚动条按钮和轨道/拇指更新滚动条。
  3. 根据添加/删除/移动/调整子控件更新滚动条。
  4. 创建自动滚动边距。
  5. 影响滚动条可见性的客户端区域限制。
  6. 等等...
另一种简单的方法可能是创建一个新的UserControl。这将允许我们使用Visual Studio设计器来简化配置滚动条按钮和轨道。
无论采取哪种方法,都需要了解ScrollableControl的内部工作方式,以提供舒适的用户体验。

1
这是关于C#和与Cody Gray的回答https://dev59.com/uFLTa4cB1Zd3GeqPZVB9#4326046相关的文章。我想在那里直接评论,但我没有足够的声望。
如果您像我一样正在遵循Cody Gray的答案中的文章,那么为了隐藏滚动条,您将使用内部和外部面板。这不是最佳解决方案,而且相当“奇怪和破解”。我遇到了类似水平滚动条的问题,并设法找到了解决方法,同样的方法也适用于垂直滚动。这是一种稍微不那么破解的方法(但差别不大),可以通过以下代码实现:
    panel1.AutoScroll = false;

    panel1.VerticalScroll.Maximum = 0;
    panel1.VerticalScroll.Visible = false;

    panel1.HorizontalScroll.Maximum = 0;
    panel1.HorizontalScroll.Visible = false;

    panel1.AutoScroll = true;

注意:从上面的代码中选择垂直或水平或两者兼有。 将AutoScroll设置为false然后再设置为true非常关键,否则设置不会应用或自动滚动被禁用。 将垂直/水平滚动最大值设置为0似乎是唯一需要的事情,但将可见性设置为false似乎并没有影响它(根本不起作用,但也许我错过了什么)。

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