1

How can you detect exactly when the scroll bar appears in a UserControl? Is there an event for this?

caesay
  • 16,932
  • 15
  • 95
  • 160
  • @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 Answers4

2

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.

John Knoeller
  • 33,512
  • 4
  • 61
  • 92
  • 1
    Iffy, 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
2

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

caesay
  • 16,932
  • 15
  • 95
  • 160
1

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.

Lee Taylor
  • 7,761
  • 16
  • 33
  • 49
Kenny
  • 11
  • 3
0

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.

Bryan
  • 91
  • 8