In WPF, is there a way to make a StackPanel with aligned columns like a Grid?

Matt picture Matt · Aug 29, 2012 · Viewed 17.3k times · Source

For example I could do something like this:

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="Auto" />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Width="Auto">
        <RowDefinition Width="Auto">
        <RowDefinition Width="Auto">
    </Grid.RowDefinitions>
    <TextBlock Grid.Row="0" Grid.Column="0">Header 1</TextBlock>
    <TextBox Grid.Row="0" Grid.Column="1" MaxLines="1" />
    <Button Grid.Row="0" Grid.Column="2">Send</Button>
    <Button Grid.Row="0" Grid.Column="3">Save</Button>
    <TextBlock Grid.Row="1" Grid.Column="0">Header 2</TextBlock>
    <TextBox Grid.Row="1" Grid.Column="1" MaxLines="1" />
    <Button Grid.Row="1" Grid.Column="2">Send</Button>
    <Button Grid.Row="1" Grid.Column="3">Save</Button>
    <TextBlock Grid.Row="2" Grid.Column="0">Header 3</TextBlock>
    <TextBox Grid.Row="2" Grid.Column="1" MaxLines="1" />
    <Button Grid.Row="2" Grid.Column="2">Send</Button>
    <Button Grid.Row="2" Grid.Column="3">Save</Button>
</Grid>

Or I could do something like this:

<StackPanel>
    <StackPanel Orientation="Horizontal">
        <TextBlock>Header 1</TextBlock>
        <TextBox MaxLines="1" />
        <Button>Send</Button>
        <Button>Save</Button>
    </StackPanel>
    <StackPanel Orientation="Horizontal">
        <TextBlock>Header 2</TextBlock>
        <TextBox MaxLines="1" />
        <Button>Send</Button>
        <Button>Save</Button>
    </StackPanel>
    <StackPanel Orientation="Horizontal">
        <TextBlock>Header 3</TextBlock>
        <TextBox MaxLines="1" />
        <Button>Send</Button>
        <Button>Save</Button>
    </StackPanel>
<StackPanel>

Except that I want to be able to easily manipulate the rows (add new rows, move rows around, etc.) just like in the StackPanel, while keeping the columns aligned properly just like in the Grid.

Answer

Overlord Zurg picture Overlord Zurg · Sep 23, 2014

Here's a class I just threw together for approximately the same purpose - essentially I wanted to be able to put a bunch of labels in the left column and values of different types (text box, dropdown, etc) on the right.

It only has two columns, but it could be adapted to different numbers.

public class LabelValueGrid : Grid
{
    public LabelValueGrid()
        : base()
    {
        ColumnDefinitions.Add(new ColumnDefinition());
        ColumnDefinitions.Add(new ColumnDefinition());
        ColumnDefinitions[0].Width = new System.Windows.GridLength(1, System.Windows.GridUnitType.Auto);
        ColumnDefinitions[1].Width = new System.Windows.GridLength(1, System.Windows.GridUnitType.Star);
    }

    protected override void OnVisualChildrenChanged(System.Windows.DependencyObject visualAdded, System.Windows.DependencyObject visualRemoved)
    {
        base.OnVisualChildrenChanged(visualAdded, visualRemoved);

        int curRow = -1;
        int curCol = 1;

        RowDefinitions.Clear();

        if (Children != null)
            foreach (System.Windows.UIElement curChild in Children)
            {
                if (curCol == 0)
                    curCol = 1;
                else
                {
                    curCol = 0;
                    curRow++;
                    RowDefinitions.Add(new RowDefinition() {Height = new System.Windows.GridLength(1, System.Windows.GridUnitType.Auto)});
                }

                Grid.SetRow(curChild, curRow);
                Grid.SetColumn(curChild, curCol);
            }

        RowDefinitions.Add(new RowDefinition() {Height = new System.Windows.GridLength(1, System.Windows.GridUnitType.Star)});
    }
}

I can use this like a grid, except I just list the controls and then it alternates the columns automatically:

<local:LabelValueGrid>
    <TextBlock Text="Label1"/>
    <TextBox Name="value1"/>
    <TextBlock Text="Label2"/>
    <TextBox Name="value2"/>
</local:LabelValueGrid>