25
votes

I created a dialog with three buttons. I put those buttons in a FlowLayoutPanel with FlowDirection set to TopDown.

I attempted to Size the buttons to the width of the panel and then set the Anchor to Left+Top+Right. This seems to have no effect in a FlowLayoutPanel.

Is there a simple solution to this? I know I can use the FlowLayoutPanel's OnResize event, and go that direction, but was hoping for a design-time setting.

4
FlowLayoutPanel is closest to WPF's StackPanel. TableLayoutPanel is more like WPF's Grid. - Colonel Panic

4 Answers

23
votes

You can use the TableLayoutPanel, if the explicit row management isn't too annoying.

20
votes

To achieve this layout, merely set all the following properties on a FlowLayoutPanel:

AutoScroll = True
FlowDirection = TopDown
WrapContents = False

Check this for details

6
votes

A FlowLayoutPanel is probably the most appropriate control. It avoids the row management aspect of TableLayoutPanel.

0
votes

Almost a decade late, sorry, but I thought I'd share the extension I just wrote to automatically resize children of a FlowLayoutPanel in the non-flow direction. For example, if the panel's FlowDirection is TopDown, you can't anchor children to Left and Right (it shrinks the child to zero width), but if you call this extension on the panel after InitializeComponent(), you'll get the same effect as if you were able to.

It also has the capability to designate one child to fill in the remaining space in the flow direction as well.

/// <summary>
/// Sets up children of a FlowLayoutPanel to auto-resize with the panel in the non-flow dimension.
/// (This is a workaround for the lack of support for anchoring both sides of a child inside FlowLayoutPanel.)
/// Optionally also resizes one control to fill remaining space in the flow dimension.
/// </summary>
/// <param name="fillControl">Optional child control to fill remaining space in the flow direction.</param>
public static void AutoSizeChildren(this FlowLayoutPanel panel, Control fillControl = null) {
    // wrapping does not make sense with auto-resizing
    panel.WrapContents = false;

    var isVertical = panel.FlowDirection.In(FlowDirection.TopDown, FlowDirection.BottomUp);
    int dim(Control c, bool flowDir = false) => isVertical ^ flowDir ? c.Width : c.Height;
    void setDim(Control c, int size, bool flowDir = false) {
        if (isVertical ^ flowDir)
            c.Width = size;
        else
            c.Height = size;
    }
    var children = panel.Controls.Cast<Control>().ToList();
    var relSizes = children.ToDictionary(c => c, c => dim(c) - dim(panel));

    // update relative size when controls are resized
    var isPanelResizing = false;
    foreach (var child in children) {
        child.Resize += (s, e) => {
            if (!isPanelResizing)
                relSizes[child] = dim(child) - dim(panel);
        };
    }

    // resize children when panel is resized
    panel.Resize += (s, e) => {
        isPanelResizing = true;
        foreach (var child in children)
            setDim(child, dim(panel) + relSizes[child]);
        isPanelResizing = false;
    };

    if (fillControl != null) {
        // add the size difference between the panel and its children to the fillControl
        void sizeFill() {
            var childrenSize = children.Sum(c => dim(c, true) + (isVertical ? c.Margin.Vertical : c.Margin.Horizontal));
            var diff = dim(panel, true) - childrenSize - (isVertical ? panel.Padding.Vertical : panel.Padding.Horizontal);
            if (diff != 0)
                setDim(fillControl, dim(fillControl, true) + diff, true);
        }
        panel.Resize += (s, e) => sizeFill();
        foreach (var child in children)
            child.Resize += (s, e) => sizeFill();
    }
}