如何在WinForms中强制垂直滚动条始终从AutoScroll可见?
将VS2010和.NET 4.0与C#和WinForms一起使用: 我总是希望在我的面板上显示一个垂直滚动条,作为一个禁用的滚动条(当它不需要时),并在可以使用时显示一个启用的滚动条 所以它就像一个混合的自动滚动条。我尝试过使用VScrollBars,但我不知道在哪里放置它们来实现这一点 基本上,我有一个用户控件,作为控件的“文档”,它的大小会发生变化,所以当使用自动滚动时,它可以完美地工作。当用户控件不适合时,滚动条会出现,用户可以将其上下移动 它本质上就像一个web浏览器。然而,重画控件需要很长的时间(它是一个包含许多字段和按钮的窗体,在一个面板的网格中的组中):p 因此,无论如何,当autoscroll启用垂直滚动条时,需要一段时间来重新绘制窗口。我希望始终显示如上所示的垂直滚动条(具有启用/禁用功能)如何在WinForms中强制垂直滚动条始终从AutoScroll可见?,winforms,scrollbars,autoscroll,Winforms,Scrollbars,Autoscroll,将VS2010和.NET 4.0与C#和WinForms一起使用: 我总是希望在我的面板上显示一个垂直滚动条,作为一个禁用的滚动条(当它不需要时),并在可以使用时显示一个启用的滚动条 所以它就像一个混合的自动滚动条。我尝试过使用VScrollBars,但我不知道在哪里放置它们来实现这一点 基本上,我有一个用户控件,作为控件的“文档”,它的大小会发生变化,所以当使用自动滚动时,它可以完美地工作。当用户控件不适合时,滚动条会出现,用户可以将其上下移动 它本质上就像一个web浏览器。然而,重画控件需要
如果有人能提供帮助,我已经阅读了很多关于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
逻辑仅更新滚动边界,并且在所有内容都适合滚动条时不会禁用滚动条。是否有方法实现启用/禁用除此之外,还有什么令人讨厌的行为?