How can you detect exactly when the scroll bar appears in a UserControl? Is there an event for this?
-
@Mitch, one reason is to adjust how the layout looks. This is a need if you've implemented your own layout method for the control where it can grow vertically, yet remain roughly the same width, if you don't shrink the consumed width the horizontal scroll bar will show too. – Jason D Mar 07 '10 at 08:17
4 Answers
They can only appear when the control is resized or the amount of data in the control increases. Since you get notifications of resize, and adding data is up to you. It's easy to add code to test for the scrollbar in the few places where their visibility can change. There's really no need to have a special notification.

- 33,512
- 4
- 61
- 92
-
1Iffy, the size of the control doesn't actually change. Only the client size changes. That does generate a Resize event, WF seems to use it to trigger a layout recalc. Not sure if it does so consistently. – Hans Passant Mar 07 '10 at 11:41
I ended up using the Layout
event, and checking if the scrollbars were currently shown or not. A Layout
event is sent when the scroll bar visibility changes.
This is more reliable than listening to the size of the window, because the size of the window is not the only thing that can cause the scrollbars to appear.
https://msdn.microsoft.com/en-us/library/system.windows.forms.control.layout(v=vs.110).aspx

- 16,932
- 15
- 95
- 160
Scroll bars are finicky
Working with scroll bars is often arduous. The Layout
event solution is correct but I want to add my additional research to the knowledgebase.
I'm attempting to automatically change the width of multiple UserControl
inside a custom UserControl
that inherits from FlowLayoutPanel
. I want a vertical scroll bar to appear only when the list is longer than the panel size. No horizontal scroll bar ever. Your implementation may differ slightly but the bulk of code will be similar and face similar issues.
ScrollableControl
In order for a UserControl
to have a scroll bar appear, it must inherit from ScrollableControl. Both Panel
and ContainerControl
fit this criteria.
ScrollableControl
only contains the Scroll
event. This event occurs when scrolling but not upon appearance of the scroll bar.
Rather, the Layout Event found inside Control
will occur when a control should reposition its child controls. This includes resizing, child resizing, and parent resizing. I would recommend using this event rather than manually checking for resize to avoid unwanted and inconsistent behavior.
Detecting a scroll bar
To detect when the scroll bar should appears, I count the number of controls in the FlowLayoutPanel
and compare it against the number of "visible controls". Visible controls are those which intersect the border area of the panel.
private void RichFlowPanel_Layout(object sender, LayoutEventArgs e)
{
var controls = Controls.Cast<Control>().OrderBy(x => x.Top);
var visibles = controls.Where(l => ClientRectangle.IntersectsWith(l.Bounds));
if (visibles.Count() <= Controls.Count)
{
// A scrollbar exists
}
else
{
// A scrollbar does not exist
}
}
Derived from this answer.
Controlling a scroll bar
A scroll bar can automatically show/hide by setting AutoScroll=true
. This will also display the horizontal scrollbar if there isn't space for the vertical scrollbar. AutoScroll
opens a Pandora's box of scrollbar issues & bugs. In order to keep the horizontal scrollbar hidden, AutoScroll must be false. This answer outlines a work around for keeping the horizontal hidden.
Specifically
panel.HorizontalScroll.Maximum = 0;
HScroll = false;
panel.VerticalScroll.Visible = false;
will hide the horizontal scroll bar.
The usage and odd behavior of HScroll
is covered in this answer.
Combining what we've learned
The following event method is attached to the Layout
event inside my custom user control extending FlowLayoutPanel
.
Point prevPosition;
private void RichFlowPanel_Layout(object sender, LayoutEventArgs e)
{
var controls = Controls.Cast<Control>().OrderBy(x => x.Top);
var visibles = controls.Where(l => ClientRectangle.IntersectsWith(l.Bounds));
prevPosition = AutoScrollPosition;
if (visibles.Count() <= Controls.Count)
{
Console.WriteLine("showing scroll bar" + " V: " + visibles.Count() + " C: " + Controls.Count);
VerticalScroll.Visible = true;
// Insert method here to tell children controls to resize
HorizontalScroll.Maximum = 0;
HScroll = false;
HorizontalScroll.Visible = false;
}
else
{
Console.WriteLine("hiding scroll bar" + " V: " + visibles.Count() + " C: " + Controls.Count);
VScroll = false;
VerticalScroll.Visible = false;
// Insert method here to tell children controls to resize
HorizontalScroll.Maximum = 0;
HScroll = false;
HorizontalScroll.Visible = false;
}
AutoScrollPosition = new Point(Math.Abs(AutoScrollPosition.X), Math.Abs(prevPosition.Y));
}
Additionally, InitalializeComponents()
of the FlowLayoutPanel
for completeness and because docking, AutoSize, etc. can often bring confusing behavior.
this.SuspendLayout();
//
// RichFlowPanelUserControl
//
this.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.AutoSize = true;
this.FlowDirection = System.Windows.Forms.FlowDirection.TopDown;
this.Padding = new System.Windows.Forms.Padding(3);
this.WrapContents = false;
this.Layout += new System.Windows.Forms.LayoutEventHandler(this.RichFlowPanel_Layout);
this.ResumeLayout(false);
I understand this is slightly off-topic but the combination of this information should help users on their scroll bar adventures.

- 7,761
- 16
- 33
- 49

- 11
- 3
Why not use the "ClientSizeChanged" event?
This event gets fired if the client size has changed, which is the case if a scrollbar is added.

- 91
- 8