C# 隐藏滚动条,同时允许在FlowLayoutPanel中使用鼠标滚轮滚动

C# 隐藏滚动条,同时允许在FlowLayoutPanel中使用鼠标滚轮滚动,c#,.net,winforms,flowlayoutpanel,C#,.net,Winforms,Flowlayoutpanel,我正在尝试创建一个动态面板,在那里我可以添加控件并在控件离开面板高度时滚动,同时隐藏滚动条 我正在使用FlowLayoutPanel,并不断向其中添加自定义面板,将其宽度设置为父容器的宽度 我还将其AutoScroll属性设置为true 然而,仍然存在一个问题。如何隐藏该死的滚动条?他们两个 我尝试了以下方法: this.lobbiesPanel.AutoScroll = false; this.lobbiesPanel.HorizontalScroll.Visible = false; thi

我正在尝试创建一个动态面板,在那里我可以添加控件并在控件离开面板高度时滚动,同时隐藏滚动条

我正在使用FlowLayoutPanel,并不断向其中添加自定义面板,将其
宽度设置为父容器的
宽度

我还将其
AutoScroll
属性设置为
true

然而,仍然存在一个问题。如何隐藏该死的滚动条?他们两个

我尝试了以下方法:

this.lobbiesPanel.AutoScroll = false;
this.lobbiesPanel.HorizontalScroll.Visible = false;
this.lobbiesPanel.VerticalScroll.Visible = false;
this.lobbiesPanel.AutoScroll = true;
令我失望的是,它没有按预期的那样工作。滚动条仍然可见。 如何隐藏滚动条,同时仍然保持使用鼠标滚轮滚动的能力


因为您只需要隐藏FlowLayoutPanel的滚动条,而不是用自己的控件替换滚动条,所以您可以构建从FlowLayoutPanel派生的自定义控件

自定义控件需要一些祖先没有的功能:

  • 它需要是可选择的
  • 必须接收鼠标输入
  • 当鼠标指针悬停在子控件上时,如果鼠标滚轮旋转,它应该能够滚动,否则填充时它不会滚动
要使其可选择并接收鼠标输入,可以向其构造函数中添加:

SetStyle(ControlStyles.UserMouse | ControlStyles.Selectable, true);
要使其滚动,无论鼠标指针位于何处,它都需要预先过滤消息,可能还有消息。
您可以使用该接口在消息被发送之前对其进行预过滤并对其采取行动(这可能很棘手,您不能贪婪地学习何时需要释放或保留消息)

当收到
WM_MOUSEWHEEL
消息并显示它指向您的控件时,您可以将其发送到FlowLayoutPanel

现在,有一个骇人的部分:一个ScrollableControl非常努力地显示它的滚动条,而你(某种程度上)需要它们,因为这个控件有一种非常奇怪的方式来计算它的
首选大小
(子控件占据的控件的总面积),并且它会根据
流动方向发生变化,另外,没有真正的方法来管理标准的滚动条:要么扔掉它们,要么隐藏它们。
或者用自己设计的控件替换它们,但这完全是另一回事

要隐藏滚动条,常用的方法是调用函数。
int wBar
参数指定要隐藏/显示的滚动条。
boolbshow
参数指定是显示(
true
)还是隐藏(
false
)这些滚动条

  • FlowLayoutPanel试图在特定条件下显示其滚动条,因此您需要捕获一些特定的消息,并每次调用
    ShowScrollBar
    (您不能只调用一次此函数就忘记它)
下面是一个实现所有这些功能的测试自定义控件:
(这是工作代码,但不完全是生产级代码:我想,您必须对其进行一些处理,以使其在特定条件/用例中表现出您喜欢的行为)

使用System.ComponentModel;
使用系统图;
使用System.Runtime.InteropServices;
使用System.Windows.Forms;
[设计分类(“代码”)]
公共类FlowLayoutPanelNoScrollbars:FlowLayoutPanel,IMessageFilter
{
公共流程布局PanelNoScrollBars(){
SetStyle(ControlStyles.UserMouse | ControlStyles.Selective,true);
}
已创建受保护的重写无效OnHandleCreated(EventArgs e){
碱基。根据HandleCreated(e);
Application.AddMessageFilter(此);
this.VerticalScroll.LargeChange=60;
this.VerticalScroll.SmallChange=20;
this.horizontalcoll.LargeChange=60;
this.horizontalcoll.SmallChange=20;
}
受保护的覆盖无效OnHandleDestroyed(事件参数e)
{
碱基。经处理的雌雄同体(e);
Application.RemoveMessageFilter(此);
}
受保护的覆盖无效WndProc(参考消息m)
{
基准WndProc(参考m);
开关(m.Msg){
案例WM_油漆:
案例WM_擦除BKGND:
案例WM_NCCALCSIZE:
如果(设计模式| |!自动滚动)中断;
ShowScrollBar(this.Handle,SB\u SHOW\u BOTH,false);
打破
案例WM_鼠标滚轮:
//处理其他特定情况下的鼠标滚轮
int delta=(int)(m.WParam.ToInt64()>>16);
int方向=数学符号(增量);
ShowScrollBar(this.Handle,SB\u SHOW\u BOTH,false);
打破
}
}
公共bool预过滤器消息(参考消息m)
{
开关(m.Msg){
案例WM_鼠标滚轮:
案例WM_鼠标滚轮:
如果(设计模式| | |!自动滚动)返回false;

如果(VerticalScroll.Maximum helps)?这确实很有用,但我想不使用按钮进行滚动。只有一个鼠标滚轮。效果很好。我只有一条评论。我已将ShowScrollBar调用中的wBar参数更改为SB_SHOW_VERT,效果很好!唯一的问题是当面板“过载”时使用控件时,水平滚动条会立即出现,然后添加另一个面板后,它不再显示。我不会同时使用SB_SHOW_,因为出于某种原因,它会弄乱面板内部的图形并损坏一些组件。有没有办法解决此问题?除此之外,回答很好,谢谢!嗨,在编辑窗口中再次调用ShowScrollBar就像修复它的东西一样。非常感谢!很好的解决方案。我很高兴它能工作。如果你发现其他缺陷,请告诉我。
using System.ComponentModel;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;

[DesignerCategory("code")]
public class FlowLayoutPanelNoScrollbars : FlowLayoutPanel, IMessageFilter
{
    public FlowLayoutPanelNoScrollbars() {
        SetStyle(ControlStyles.UserMouse | ControlStyles.Selectable, true);
    }

    protected override void OnHandleCreated(EventArgs e) {
        base.OnHandleCreated(e);
        Application.AddMessageFilter(this);

        this.VerticalScroll.LargeChange = 60;
        this.VerticalScroll.SmallChange = 20;

        this.HorizontalScroll.LargeChange = 60;
        this.HorizontalScroll.SmallChange = 20;
    }

    protected override void OnHandleDestroyed(EventArgs e) 
    {
        base.OnHandleDestroyed(e);
        Application.RemoveMessageFilter(this);
    }

    protected override void WndProc(ref Message m)
    {
        base.WndProc(ref m);
        switch (m.Msg) {
            case WM_PAINT:
            case WM_ERASEBKGND:
            case WM_NCCALCSIZE:
                if (DesignMode || !AutoScroll) break;
                ShowScrollBar(this.Handle, SB_SHOW_BOTH, false);
                break;
            case WM_MOUSEWHEEL:
                // Handle Mouse Wheel for other specific cases
                int delta = (int)(m.WParam.ToInt64() >> 16);
                int direction = Math.Sign(delta);
                ShowScrollBar(this.Handle, SB_SHOW_BOTH, false); 
                break;
        }
    }

    public bool PreFilterMessage(ref Message m)
    {
        switch (m.Msg) {
            case WM_MOUSEWHEEL:
            case WM_MOUSEHWHEEL:
                if (DesignMode || !AutoScroll) return false;
                if (VerticalScroll.Maximum <= ClientSize.Height) return false;
                // Should also check whether the ForegroundWindow matches the parent Form.
                if (RectangleToScreen(ClientRectangle).Contains(MousePosition)) {
                    SendMessage(this.Handle, WM_MOUSEWHEEL, m.WParam, m.LParam);
                    return true;
                }
                break;
            case WM_LBUTTONDOWN:
                // Pre-handle Left Mouse clicks for all child Controls
                if (RectangleToScreen(ClientRectangle).Contains(MousePosition)) {
                    // Do something here, if needed
                    return true; // <= eventually, with caution
                }
                return false;
        }
        return false;
    }

    private const int WM_PAINT = 0x000F;
    private const int WM_ERASEBKGND = 0x0014;
    private const int WM_NCCALCSIZE = 0x0083;
    private const int WM_LBUTTONDOWN = 0x0201;
    private const int WM_MOUSEWHEEL = 0x020A;
    private const int WM_MOUSEHWHEEL = 0x020E;
    private const int SB_SHOW_VERT = 0x1;
    private const int SB_SHOW_BOTH = 0x3; 

    [DllImport("user32.dll", SetLastError = true)]
    private static extern bool ShowScrollBar(IntPtr hWnd, int wBar, bool bShow);

    [DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern int SendMessage(IntPtr hWnd, uint uMsg, IntPtr wParam, IntPtr lParam);
}