0

I've got a Panel that contains many smaller panels.
The containing panel has AutoScroll set to true so that when there are too many small panels I can scroll down.
The small panel's width is always set to the clientsize width of the container (allowing for borders etc) so that no horizontal scrollbar should ever be displayed.
This seems to mostly be the case unless the last sub-panel is touching the bottom of the containing panel, then the horizontal scrollbar appears when I don't want it to!

I've tried changing the properties to disable it but this seemingly has no effect.
I've tried always showing the vertical scrollbar (as this would be acceptable), but it only briefly showed a really ugly bar that then disappeared and was replaced by the native one.
I can see other people with the same problem but no solution.

Paste the code below into a new Winform and try the following steps:

  1. Press the 'Add' button 4 times to get 4 panels, they should go off the bottom of the container.
  2. Vertically resize the form so that there is some whitespace after the last panel.
  3. Now carefully vertically resize the form so that the bottom of the last panel touches the bottom of the container. It's around here that the scrollbar should appear, it might take a bit of 'wiggling'.

    static List<Panel> listOfPanels;
    static Panel panel;
    static bool flipflop;
    
    private void Form1_Load(object sender, EventArgs e)
    {
        Height = 400;
        Width = 400;
    
        listOfPanels = new List<Panel>();
    
        panel = new Panel()
        {
            Height = this.ClientSize.Height - 20,
            Width = 200,
            Top = 10,
            Left = 10,
            BackColor = Color.White,
            BorderStyle = BorderStyle.FixedSingle,
            Padding = Padding.Empty,
            Margin = Padding.Empty,
            Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Bottom,
        };
    
        // I'VE TRIED DISABLING IT HERE!
        panel.AutoScroll = false;
        panel.HorizontalScroll.Enabled = false;
        panel.HorizontalScroll.Visible = false;
        panel.AutoScroll = true;
    
        panel.Resize += panel_Resize;
    
        Button button = new Button()
        {
            Text = @"Add",
            Size = new Size(100, 50),
            Top = 10,
            Left = 20 + panel.Width
        };
        button.Click += button_Click;
    
        Controls.Add(panel);
        Controls.Add(button);
    }
    
    void panel_Resize(object sender, EventArgs e)
    {
        renderSubPanels();
    }
    
    void button_Click(object sender, EventArgs e)
    {
        Panel subPanel = new Panel()
        {
            Height = 100,
            BackColor = flipflop ? Color.PeachPuff : Color.PowderBlue,
            Top = (listOfPanels.Count * 100) - Math.Abs(panel.AutoScrollPosition.Y)
        };
    
        listOfPanels.Add(subPanel);
    
        flipflop = !flipflop;
    
        panel.Controls.Add(subPanel);
    
        renderSubPanels();
    }
    
    void renderSubPanels()
    {
        panel.SuspendLayout();
        bool verticalScrollVisible = listOfPanels.Count * 100 > panel.ClientSize.Height;
        foreach (Panel p in listOfPanels)
        {
            if (verticalScrollVisible)
            {
                p.Width = panel.Width - System.Windows.Forms.SystemInformation.VerticalScrollBarWidth - 2;
            }
            else
            {
                p.Width = panel.Width - 2;
            }
    
            p.Top = (listOfPanels.IndexOf(p) * 100) - Math.Abs(this.AutoScrollPosition.Y);
        }
        panel.ResumeLayout();
    }
    

In my real program it's actually a custom panel I'm working with so I'm open to ideas. I just want the pesky thing to go away!
Thanks!

Community
  • 1
  • 1
Equalsk
  • 7,954
  • 2
  • 41
  • 67
  • I still don't get what you are trying to do. So if you try to resize the main form and if the parent panel is long enough for all sub panels, the vertical scroll on the parent panel will be disabled, am I right? – ngunha02 Oct 15 '15 at 16:45

2 Answers2

2

Comment out the AutoScroll:

//panel.AutoScroll = true;

Then add this code:

panel.ControlAdded += panel_ControlAdded;

void panel_ControlAdded(object sender, ControlEventArgs e) {
  panel.AutoScrollMinSize = new Size(0,
    panel.Controls.Cast<Control>().Sum(x => x.Height));
}

A FlowLayoutPanel would probably do this for you.

Your current code is doing too much unnecessary work. You do not need the renderSubPanels() method, so you can comment that out and your positioning of the Top position didn't work when the scroll position wasn't in the home position. Just set the Anchor properties of the subpanel and it will correctly account for the VerticalScrollBar being visible or not:

Panel subPanel = new Panel() {
  Height = 100,
  BackColor = flipflop ? Color.PeachPuff : Color.PowderBlue,

  // updated properties:
  Top = panel.Controls.Cast<Control>().Sum(x => x.Height) + panel.AutoScrollPosition.Y,
  Width = panel.ClientSize.Width, 
  Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right
};
LarsTech
  • 80,625
  • 14
  • 153
  • 225
  • Unfortunately this doesn't seem to work, I still get the horizontal scrollbar. I'd try a FlowLayoutPanel as a last resort, but it would mean rewriting a large part of the control which I'd rather avoid. The above is just a very basic sample of the issue, it's far from the real thing :-) – Equalsk Oct 15 '15 at 16:29
  • @Equalsk I do not get a horizontal scroll bar with the changes I recommended. Remember to turn the AutoScroll off on the panel control. You can also ignore those `panel.HorizontalScroll` properties, too. – LarsTech Oct 15 '15 at 16:31
  • Did you definitely get it before you made the changes? It is within the tolerance of a few pixels. I've definitely commented all instances of the AutoScroll properties like you say so it's not that. I'm on Windows 8, perhaps it's OS specific? – Equalsk Oct 15 '15 at 16:35
  • @Equalsk I am only working on the code you supplied. With AutoScroll turned off and the AutoScrollMinSize.Width = 0, you should never see the horizontal scrollbar. That's what my code does. – LarsTech Oct 15 '15 at 16:40
  • Hmm, not sure what else to tell you. I was only working with the example as well. Don't worry about the top not working, it does in the real version, this is just a demo for the purposes of this post. Thanks for your time! – Equalsk Oct 16 '15 at 08:04
1

I think you can get the behavior you want by using panel.PerformLayout instead of SuspendLayout/ResumeLayout:

    void renderSubPanels()
    {
        //panel.SuspendLayout();
        bool verticalScrollVisible = listOfPanels.Count * 100 > panel.ClientSize.Height;
        foreach (Panel p in listOfPanels)
        {
            if (verticalScrollVisible)
            {
                p.Width = panel.Width - System.Windows.Forms.SystemInformation.VerticalScrollBarWidth - 2;
            }
            else
            {
                p.Width = panel.Width - 2;
            }

            p.Top = (listOfPanels.IndexOf(p) * 100) - Math.Abs(this.AutoScrollPosition.Y);
        }
        //panel.ResumeLayout();
        panel.PerformLayout();
    }

This seems to work for me.

Erik
  • 5,355
  • 25
  • 39