如何在WinForms中强制垂直滚动条始终从AutoScroll可见?

如何在WinForms中强制垂直滚动条始终从AutoScroll可见?,winforms,scrollbars,autoscroll,Winforms,Scrollbars,Autoscroll,将VS2010和.NET 4.0与C#和WinForms一起使用: 我总是希望在我的面板上显示一个垂直滚动条,作为一个禁用的滚动条(当它不需要时),并在可以使用时显示一个启用的滚动条 所以它就像一个混合的自动滚动条。我尝试过使用VScrollBars,但我不知道在哪里放置它们来实现这一点 基本上,我有一个用户控件,作为控件的“文档”,它的大小会发生变化,所以当使用自动滚动时,它可以完美地工作。当用户控件不适合时,滚动条会出现,用户可以将其上下移动 它本质上就像一个web浏览器。然而,重画控件需要

将VS2010和.NET 4.0与C#和WinForms一起使用:

我总是希望在我的面板上显示一个垂直滚动条,作为一个禁用的滚动条(当它不需要时),并在可以使用时显示一个启用的滚动条

所以它就像一个混合的自动滚动条。我尝试过使用VScrollBars,但我不知道在哪里放置它们来实现这一点

基本上,我有一个用户控件,作为控件的“文档”,它的大小会发生变化,所以当使用自动滚动时,它可以完美地工作。当用户控件不适合时,滚动条会出现,用户可以将其上下移动

它本质上就像一个web浏览器。然而,重画控件需要很长的时间(它是一个包含许多字段和按钮的窗体,在一个面板的网格中的组中):p

因此,无论如何,当autoscroll启用垂直滚动条时,需要一段时间来重新绘制窗口。我希望始终显示如上所示的垂直滚动条(具有启用/禁用功能)


如果有人能提供帮助,我已经阅读了很多关于autoscroll主题的帖子,但是没有人问过我的问题,我想不出解决方案。

您可以使用面板的自动滚动功能,只需向它发送一条windows消息来显示垂直滚动条:

<DllImport("user32.dll")> _
Public Shared Function ShowScrollBar(ByVal hWnd As System.IntPtr, ByVal wBar As Integer, ByVal bShow As Boolean) As Boolean
End Function

Private Const SB_VERT As Integer = 1


Public Sub New()

    ' This call is required by the designer.
    InitializeComponent()

    ShowScrollBar(Panel1.Handle, SB_VERT, True)
End Sub
_
公共共享函数ShowScrollBar(ByVal hWnd作为System.IntPtr,ByVal wBar作为整数,ByVal bShow作为布尔值)作为布尔值
端函数
Private Const SB_VERT作为整数=1
公共分新()
'设计器需要此调用。
初始化组件()
显示滚动条(Panel1.Handle,SB_VERT,True)
端接头
滚动条将显示并显示为可以滚动,但它在实际准备滚动之前不会执行任何操作。如果禁用它,它将不会自动重新启用,因此这可能是最好的方法

此外,为了在调整大小时提高性能,您可以在更新之前和更新完成时调用面板。

C#competent_Tech答案的版本

using System.Runtime.InteropServices; 

public class MyUserControl : UserControl
{
    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool ShowScrollBar(IntPtr hWnd, int wBar, bool bShow);

    private enum ScrollBarDirection
    {
        SB_HORZ = 0,
        SB_VERT = 1,
        SB_CTL = 2,
        SB_BOTH = 3
    }

    public MyUserControl()
    {
        InitializeComponent();
        ShowScrollBar(this.Handle, (int) ScrollBarDirection.SB_VERT, true);
    }
}

这就是我解决这个问题的方法。我的情况是,我有一个面板夹在另外三个面板之间,在任何方向上都没有自由度。我需要这个面板足够大,整个结构都能从我的1920x1080屏幕上消失。 解决办法其实很简单。 对于需要滚动条的面板,请将AutoScroll属性设置为true。然后,在其上添加另一个控件,位于最右侧最下方位置(右下方位置)。我选择的控件是一个标签,我将其设置为不可见…仅此而已。 现在我的面板占据了它的限制区域,但我可以滚动到我需要的大小,并根据我需要的大小使用它。 如果您只需要水平滚动条,请在左外侧添加不可见控件,垂直方向仅在底部下方较远的位置


面板的实际大小是显示时限制的大小,但虚拟大小由不可见控件决定。

对我有效的方法是覆盖
CreateParams
调用并启用
WS\u VSCROLL
样式

public class VerticalFlowPanel : FlowLayoutPanel
{
    protected override CreateParams CreateParams
    {
        get
        {
            var cp = base.CreateParams;
            cp.Style |= 0x00200000; // WS_VSCROLL
            return cp;
        }
    }
}

AutoScroll
逻辑现在将调整滚动边界,而不会隐藏滚动条。

每当面板的内置滚动条不可见时,此代码将绘制禁用的垂直滚动条。这些代码假定

AutoScroll = true;
AutoSize   = false;
以下代码是速度优化的。它在OnPaint()中执行的操作尽可能少

从面板派生一个类。 添加以下成员变量:

// NOTE: static variables are not thread safe. 
// But as we have only one GUI thread this does not matter.
static IntPtr mh_ScrollTheme   = IntPtr.Zero;
static int    ms32_ScrollWidth = SystemInformation.VerticalScrollBarWidth;

Win32.RECT mk_ScrollTop;
Win32.RECT mk_ScrollBot;   // coordinates of top scrollbar button
Win32.RECT mk_ScrollShaft; // coordinates of bottom scrollbar button
然后覆盖OnSizeChanged:

protected override void OnSizeChanged(EventArgs e)
{
    base.OnSizeChanged(e);

    Win32.RECT k_ScrollBar = new Win32.RECT(ClientRectangle);
    k_ScrollBar.Left = k_ScrollBar.Right - ms32_ScrollWidth;

    mk_ScrollTop   = new Win32.RECT(k_ScrollBar);
    mk_ScrollBot   = new Win32.RECT(k_ScrollBar);
    mk_ScrollShaft = new Win32.RECT(k_ScrollBar);

    int s32_Upper = k_ScrollBar.Top    + ms32_ScrollWidth;
    int s32_Lower = k_ScrollBar.Bottom - ms32_ScrollWidth;

    mk_ScrollTop  .Bottom = s32_Upper;
    mk_ScrollBot  .Top    = s32_Lower;
    mk_ScrollShaft.Top    = s32_Upper;
    mk_ScrollShaft.Bottom = s32_Lower;
}
并在需要时绘制滚动条:

protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint(e);

    if (VScroll)
        return; // The 'real' scrollbar is visible
            
    if (mh_ScrollTheme == IntPtr.Zero)
        mh_ScrollTheme = Win32.OpenThemeData(Handle, "SCROLLBAR");

    if (mh_ScrollTheme == IntPtr.Zero)
        return; // The user has disabled themes

    // Draw the disabled vertical scrollbar.
    IntPtr h_DC = e.Graphics.GetHdc();

    // Draw shaft
    const int SBP_UPPERTRACKVERT = 7;
    const int SCRBS_DISABLED     = 4;
    Win32.DrawThemeBackground(mh_ScrollTheme, h_DC, SBP_UPPERTRACKVERT, SCRBS_DISABLED, ref mk_ScrollShaft, IntPtr.Zero);

    // Draw top button
    const int SBP_ARROWBTN       = 1;
    const int ABS_UPDISABLED     = 4;
    Win32.DrawThemeBackground(mh_ScrollTheme, h_DC, SBP_ARROWBTN, ABS_UPDISABLED,   ref mk_ScrollTop,  IntPtr.Zero);

    // Draw lower button
    const int ABS_DOWNDISABLED   = 8;
    Win32.DrawThemeBackground(mh_ScrollTheme, h_DC, SBP_ARROWBTN, ABS_DOWNDISABLED, ref mk_ScrollBot,  IntPtr.Zero);

    e.Graphics.ReleaseHdc(h_DC);
}

这很难做到。控制滚动条的代码是ScrollableControl中的私有方法,无法覆盖它们。试图通过在需要滚动时隐藏的面板中停靠一个VScrollBar来伪造它,导致了我无法摆脱的小故障。我放弃了。Hans是对的。如果你想让你的东西稳定工作,d不要相信下面的答案。标准的
WS\u HSCROLL
WS\u VSCROLL
滚动条背后的逻辑在Win32 API中已经毫无希望地搞砸了(这就是
ScrollableControl
中使用的逻辑)。即使您尝试在该级别进行修复,也会出现很多问题;Windows坚持控制滚动条。此处发布的答案充其量是脆弱的,并且会产生不必要的副作用,如闪烁或布局问题。如果您想正确执行此操作,您必须自己编写大量代码。我尝试过这样做,但我失败了t似乎无关紧要。挂起/恢复布局。此方法与在VerticalScroll对象上简单运行不同吗。Visible=true?因为这样做不正确。滚动条未被修改。是的,此方法不同于设置
Visible=true
(这是我首先尝试的方法,但.Net完全忽略它)。这需要:使用System.Runtime.InteropServices;这仅在我将其放入VisibleChanged()时对我有效。它似乎仅在控件可见时生效(?)这很有效,但不需要滚动条时滚动条的样式与可以滚动条时的样式大不相同。这是一个很好且简单的解决方法,但不幸的是,
AutoScroll
逻辑仅更新滚动边界,并且在所有内容都适合滚动条时不会禁用滚动条。是否有方法实现启用/禁用除此之外,还有什么令人讨厌的行为?