6

How can I set up a WPF control to fill the available space in its parent's container, but not expand the parent?

The following snippet describes the layout I am attempting. I would like the Grid to stretch to accommodate the Expander, and I would like the ListBox only to fill the Grid. I want the ListBox's scroll bar to appear when the Grid is too small to show all the ListBoxItems.

<ScrollViewer>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition />
        </Grid.RowDefinitions>
        <ListBox Grid.Row="0" Grid.Column="0" />
        <Expander Grid.Row="0" Grid.Column="1" Header="Expander" />
    </Grid>
</ScrollViewer>

Currently what happens is that the Grid stretches to fit the entire ListBox, and the outer ScrollViewer's vertical scroll bar appears. I only want the outer scroll bar to appear when the Expander gets too big to fit on screen.

Dave Clemmer
  • 3,741
  • 12
  • 49
  • 72
sourcenouveau
  • 29,356
  • 35
  • 146
  • 243

2 Answers2

5

To solve the same problem I wrote special container class:

class FrugalContainer : Decorator
{
    protected override Size MeasureOverride(Size availableSize)
    {
        return new Size(0, 0);
    }

    protected override Size ArrangeOverride(Size arrangeSize)
    {
        // get it all
        Child.Measure(arrangeSize);
        Child.Arrange(new Rect(arrangeSize));
        return Child.RenderSize;
    }
}

Surround your ListBox by the container and the height of ListBox will be the same as of Expander.

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition />
        <ColumnDefinition />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition />
    </Grid.RowDefinitions>
    <FrugalContainer Grid.Row="0" Grid.Column="0" >
        <ListBox />
    </FrugalContainer>
    <Expander Grid.Row="0" Grid.Column="1" Header="Expander" />
</Grid>

Note that I remove Width="Auto" from column's definition because the FrugalContainer will be as small as it can. So you can't set the width or height of the parent grid's cell to Auto.

If you need autosizing, rewrite the container:

class FrugalHeightContainer : Decorator
{
    protected override Size MeasureOverride(Size availableSize)
    {
        Child.Measure(availableSize);
        return new Size(Child.DesiredSize.Width, 0);
    }

    protected override Size ArrangeOverride(Size arrangeSize)
    {
        Child.Measure(arrangeSize);
        Child.Arrange(new Rect(arrangeSize));
        return Child.RenderSize;
    }
}
Ilya Serbis
  • 21,149
  • 6
  • 87
  • 74
  • I feel a bit stupid asking this, but where do you put this custom decorater class? I put it in the app root, and it doesn't appear as an option in Xaml. – Kyeotic Feb 07 '12 at 19:26
  • very elegant solution and much better than binding MaxHeight of ListBox to the ActualHeight of parent container, which is the most widely recommended solution out there. – dmg Apr 01 '22 at 16:51
0

What is the point of the ScrollViewer? Just let the ScrollViewer in the ListBox template appear naturally when too little room is available.

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition />
    </Grid.RowDefinitions>
    <ListBox Grid.Row="0" Grid.Column="0" />
    <Expander Grid.Row="0" Grid.Column="1" Header="Expander" />
</Grid>
Kent Boogaart
  • 175,602
  • 35
  • 392
  • 393