3
votes

I'm experiencing a problem with FlowLayoutPanel while its children being resized on FlowLayoutPanel's ClientSizeChanged event.

I'm trying to make the children resize horizontally when the FlowLayoutPanel resizes. The problem is that even though the margin of the child is 0 and the padding of FlowLayoutPanel is also 0, after the execution of ClientSizeChanged event handler, the FlowLayoutPanel shows its horizontal scroll bar while the width of the child is exactly the same as FlowLayoutPanel.ClientSize.Width.

I've tried to move the code to Resize event, but I still get the same result.

This is an example to demonstrate the problem, there's one FlowLayoutPanel with the following properties changed from default:

  • AutoScroll = true
  • FlowDirection = TopDown
  • Margin = 0,0,0,0
  • Name = flow1
  • BackColor = Red

There's also a regular panel inside the FlowLayoutPanel:

  • Margin = 0,0,0,0
  • Name = panel1
  • BackColor: Blue

And finally, a timer with interval 1 which changes the width of the FlowLayoutPanel and disables itself when the HorizontalScroll.Visibile property of the FlowLayoutPanel is true and shows a message box that announces the width of panel1 and ClientSize.Width of the flow1.

Here's the code:

    private void timer1_Tick(object sender, EventArgs e)
    {
        flow1.Width -= 1;

        if (flow1.HorizontalScroll.Visible)
        {
            timer1.Enabled = false;
            MessageBox.Show("Panel.Width = " + panel1.Width.ToString() +
                ", FlowPanel.ClientWidth = " + flow1.ClientSize.Width.ToString());
        }
    }

    private void flow1_ClientSizeChanged(object sender, EventArgs e)
    {
        panel1.Width = flow1.ClientSize.Width;
    }

What's the logic behind the horizontal scroll bar being shown while no children overflows the client size? And most importantly, how to prevent it from happening?

1

1 Answers

1
votes

It is an event order problem, the layout gets calculated too soon. Automatic layout has several nasty corner-cases, it can also be bi-stable with the layout flipping back-and-forth between two solutions. You can see this in your test app, add flow1.PerformLayout(); before the MessageBox.Show() call and you'll see that the scrollbar is hidden again.

This is why the SuspendLayout() method exists. Winforms programmers don't use it enough. For a good reason, it is pretty hard to judge when you need it. And they really don't want to need it. Basic rule is that you should use it if layout must deal with more than one size changing.

Which is the real fix in your test program:

private void timer1_Tick(object sender, EventArgs e) {
    flow1.SuspendLayout();
    flow1.Width -= 1;
    flow1.ResumeLayout(true);
    // etc..
}

And you'll see that it works just fine now, you never see the message box.